how to make php websocket server accept connect with wss:// protocol - php
I am using this php class to make websocket server and i succeed to connect to websocket server using ws:// protocol but I need to connect using wss://
I am using this websocket server to build chat script and i have not informations about another websocket services like websocket.io node.js and other libraries i want to create the websocket server in pure php only
the php class is below
<?php
class PHPWebSocket
{
// maximum amount of clients that can be connected at one time
const WS_MAX_CLIENTS = 100;
// maximum amount of clients that can be connected at one time on the same IP v4 address
const WS_MAX_CLIENTS_PER_IP = 15;
// amount of seconds a client has to send data to the server, before a ping request is sent to the client,
// if the client has not completed the opening handshake, the ping request is skipped and the client connection is closed
const WS_TIMEOUT_RECV = 10;
// amount of seconds a client has to reply to a ping request, before the client connection is closed
const WS_TIMEOUT_PONG = 5;
// the maximum length, in bytes, of a frame's payload data (a message consists of 1 or more frames), this is also internally limited to 2,147,479,538
const WS_MAX_FRAME_PAYLOAD_RECV = 100000;
// the maximum length, in bytes, of a message's payload data, this is also internally limited to 2,147,483,647
const WS_MAX_MESSAGE_PAYLOAD_RECV = 500000;
// internal
const WS_FIN = 128;
const WS_MASK = 128;
const WS_OPCODE_CONTINUATION = 0;
const WS_OPCODE_TEXT = 1;
const WS_OPCODE_BINARY = 2;
const WS_OPCODE_CLOSE = 8;
const WS_OPCODE_PING = 9;
const WS_OPCODE_PONG = 10;
const WS_PAYLOAD_LENGTH_16 = 126;
const WS_PAYLOAD_LENGTH_63 = 127;
const WS_READY_STATE_CONNECTING = 0;
const WS_READY_STATE_OPEN = 1;
const WS_READY_STATE_CLOSING = 2;
const WS_READY_STATE_CLOSED = 3;
const WS_STATUS_NORMAL_CLOSE = 1000;
const WS_STATUS_GONE_AWAY = 1001;
const WS_STATUS_PROTOCOL_ERROR = 1002;
const WS_STATUS_UNSUPPORTED_MESSAGE_TYPE = 1003;
const WS_STATUS_MESSAGE_TOO_BIG = 1004;
const WS_STATUS_TIMEOUT = 3000;
// global vars
public $wsClients = array();
public $wsRead = array();
public $wsClientCount = 0;
public $wsClientIPCount = array();
public $wsOnEvents = array();
// server state functions
function wsStartServer($host, $port) {
if (isset($this->wsRead[0])) return false;
if (!$this->wsRead[0] = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) {
return false;
}
if (!socket_set_option($this->wsRead[0], SOL_SOCKET, SO_REUSEADDR, 1)) {
socket_close($this->wsRead[0]);
return false;
}
if (!socket_bind($this->wsRead[0], $host, $port)) {
socket_close($this->wsRead[0]);
return false;
}
if (!socket_listen($this->wsRead[0], 10)) {
socket_close($this->wsRead[0]);
return false;
}
$write = array();
$except = array();
$nextPingCheck = time() + 1;
while (isset($this->wsRead[0])) {
$changed = $this->wsRead;
$result = socket_select($changed, $write, $except, 1);
if ($result === false) {
socket_close($this->wsRead[0]);
return false;
}
elseif ($result > 0) {
foreach ($changed as $clientID => $socket) {
if ($clientID != 0) {
// client socket changed
$buffer = '';
$bytes = #socket_recv($socket, $buffer, 4096, 0);
if ($bytes === false) {
// error on recv, remove client socket (will check to send close frame)
$this->wsSendClientClose($clientID, self::WS_STATUS_PROTOCOL_ERROR);
}
elseif ($bytes > 0) {
// process handshake or frame(s)
if (!$this->wsProcessClient($clientID, $buffer, $bytes)) {
$this->wsSendClientClose($clientID, self::WS_STATUS_PROTOCOL_ERROR);
}
}
else {
// 0 bytes received from client, meaning the client closed the TCP connection
$this->wsRemoveClient($clientID);
}
}
else {
// listen socket changed
$client = socket_accept($this->wsRead[0]);
if ($client !== false) {
// fetch client IP as integer
$clientIP = '';
$result = socket_getpeername($client, $clientIP);
$clientIP = ip2long($clientIP);
if ($result !== false && $this->wsClientCount < self::WS_MAX_CLIENTS && (!isset($this->wsClientIPCount[$clientIP]) || $this->wsClientIPCount[$clientIP] < self::WS_MAX_CLIENTS_PER_IP)) {
$this->wsAddClient($client, $clientIP);
}
else {
socket_close($client);
}
}
}
}
}
if (time() >= $nextPingCheck) {
$this->wsCheckIdleClients();
$nextPingCheck = time() + 1;
}
}
return true; // returned when wsStopServer() is called
}
function wsStopServer() {
// check if server is not running
if (!isset($this->wsRead[0])) return false;
// close all client connections
foreach ($this->wsClients as $clientID => $client) {
// if the client's opening handshake is complete, tell the client the server is 'going away'
if ($client[2] != self::WS_READY_STATE_CONNECTING) {
$this->wsSendClientClose($clientID, self::WS_STATUS_GONE_AWAY);
}
socket_close($client[0]);
}
// close the socket which listens for incoming clients
socket_close($this->wsRead[0]);
// reset variables
$this->wsRead = array();
$this->wsClients = array();
$this->wsClientCount = 0;
$this->wsClientIPCount = array();
return true;
}
// client timeout functions
function wsCheckIdleClients() {
$time = time();
foreach ($this->wsClients as $clientID => $client) {
if ($client[2] != self::WS_READY_STATE_CLOSED) {
// client ready state is not closed
if ($client[4] !== false) {
// ping request has already been sent to client, pending a pong reply
if ($time >= $client[4] + self::WS_TIMEOUT_PONG) {
// client didn't respond to the server's ping request in self::WS_TIMEOUT_PONG seconds
$this->wsSendClientClose($clientID, self::WS_STATUS_TIMEOUT);
$this->wsRemoveClient($clientID);
}
}
elseif ($time >= $client[3] + self::WS_TIMEOUT_RECV) {
// last data was received >= self::WS_TIMEOUT_RECV seconds ago
if ($client[2] != self::WS_READY_STATE_CONNECTING) {
// client ready state is open or closing
$this->wsClients[$clientID][4] = time();
$this->wsSendClientMessage($clientID, self::WS_OPCODE_PING, '');
}
else {
// client ready state is connecting
$this->wsRemoveClient($clientID);
}
}
}
}
}
// client existence functions
function wsAddClient($socket, $clientIP) {
// increase amount of clients connected
$this->wsClientCount++;
// increase amount of clients connected on this client's IP
if (isset($this->wsClientIPCount[$clientIP])) {
$this->wsClientIPCount[$clientIP]++;
}
else {
$this->wsClientIPCount[$clientIP] = 1;
}
// fetch next client ID
$clientID = $this->wsGetNextClientID();
// store initial client data
$this->wsClients[$clientID] = array($socket, '', self::WS_READY_STATE_CONNECTING, time(), false, 0, $clientIP, false, 0, '', 0, 0);
// store socket - used for socket_select()
$this->wsRead[$clientID] = $socket;
}
function wsRemoveClient($clientID) {
// fetch close status (which could be false), and call wsOnClose
$closeStatus = $this->wsClients[$clientID][5];
if ( array_key_exists('close', $this->wsOnEvents) )
foreach ( $this->wsOnEvents['close'] as $func )
$func($clientID, $closeStatus);
// close socket
$socket = $this->wsClients[$clientID][0];
socket_close($socket);
// decrease amount of clients connected on this client's IP
$clientIP = $this->wsClients[$clientID][6];
if ($this->wsClientIPCount[$clientIP] > 1) {
$this->wsClientIPCount[$clientIP]--;
}
else {
unset($this->wsClientIPCount[$clientIP]);
}
// decrease amount of clients connected
$this->wsClientCount--;
// remove socket and client data from arrays
unset($this->wsRead[$clientID], $this->wsClients[$clientID]);
}
// client data functions
function wsGetNextClientID() {
$i = 1; // starts at 1 because 0 is the listen socket
while (isset($this->wsRead[$i])) $i++;
return $i;
}
function wsGetClientSocket($clientID) {
return $this->wsClients[$clientID][0];
}
// client read functions
function wsProcessClient($clientID, &$buffer, $bufferLength) {
if ($this->wsClients[$clientID][2] == self::WS_READY_STATE_OPEN) {
// handshake completed
$result = $this->wsBuildClientFrame($clientID, $buffer, $bufferLength);
}
elseif ($this->wsClients[$clientID][2] == self::WS_READY_STATE_CONNECTING) {
// handshake not completed
$result = $this->wsProcessClientHandshake($clientID, $buffer);
if ($result) {
$this->wsClients[$clientID][2] = self::WS_READY_STATE_OPEN;
if ( array_key_exists('open', $this->wsOnEvents) )
foreach ( $this->wsOnEvents['open'] as $func )
$func($clientID);
}
}
else {
// ready state is set to closed
$result = false;
}
return $result;
}
function wsBuildClientFrame($clientID, &$buffer, $bufferLength) {
// increase number of bytes read for the frame, and join buffer onto end of the frame buffer
$this->wsClients[$clientID][8] += $bufferLength;
$this->wsClients[$clientID][9] .= $buffer;
// check if the length of the frame's payload data has been fetched, if not then attempt to fetch it from the frame buffer
if ($this->wsClients[$clientID][7] !== false || $this->wsCheckSizeClientFrame($clientID) == true) {
// work out the header length of the frame
$headerLength = ($this->wsClients[$clientID][7] <= 125 ? 0 : ($this->wsClients[$clientID][7] <= 65535 ? 2 : 8)) + 6;
// check if all bytes have been received for the frame
$frameLength = $this->wsClients[$clientID][7] + $headerLength;
if ($this->wsClients[$clientID][8] >= $frameLength) {
// check if too many bytes have been read for the frame (they are part of the next frame)
$nextFrameBytesLength = $this->wsClients[$clientID][8] - $frameLength;
if ($nextFrameBytesLength > 0) {
$this->wsClients[$clientID][8] -= $nextFrameBytesLength;
$nextFrameBytes = substr($this->wsClients[$clientID][9], $frameLength);
$this->wsClients[$clientID][9] = substr($this->wsClients[$clientID][9], 0, $frameLength);
}
// process the frame
$result = $this->wsProcessClientFrame($clientID);
// check if the client wasn't removed, then reset frame data
if (isset($this->wsClients[$clientID])) {
$this->wsClients[$clientID][7] = false;
$this->wsClients[$clientID][8] = 0;
$this->wsClients[$clientID][9] = '';
}
// if there's no extra bytes for the next frame, or processing the frame failed, return the result of processing the frame
if ($nextFrameBytesLength <= 0 || !$result) return $result;
// build the next frame with the extra bytes
return $this->wsBuildClientFrame($clientID, $nextFrameBytes, $nextFrameBytesLength);
}
}
return true;
}
function wsCheckSizeClientFrame($clientID) {
// check if at least 2 bytes have been stored in the frame buffer
if ($this->wsClients[$clientID][8] > 1) {
// fetch payload length in byte 2, max will be 127
$payloadLength = ord(substr($this->wsClients[$clientID][9], 1, 1)) & 127;
if ($payloadLength <= 125) {
// actual payload length is <= 125
$this->wsClients[$clientID][7] = $payloadLength;
}
elseif ($payloadLength == 126) {
// actual payload length is <= 65,535
if (substr($this->wsClients[$clientID][9], 3, 1) !== false) {
// at least another 2 bytes are set
$payloadLengthExtended = substr($this->wsClients[$clientID][9], 2, 2);
$array = unpack('na', $payloadLengthExtended);
$this->wsClients[$clientID][7] = $array['a'];
}
}
else {
// actual payload length is > 65,535
if (substr($this->wsClients[$clientID][9], 9, 1) !== false) {
// at least another 8 bytes are set
$payloadLengthExtended = substr($this->wsClients[$clientID][9], 2, 8);
// check if the frame's payload data length exceeds 2,147,483,647 (31 bits)
// the maximum integer in PHP is "usually" this number. More info: http://php.net/manual/en/language.types.integer.php
$payloadLengthExtended32_1 = substr($payloadLengthExtended, 0, 4);
$array = unpack('Na', $payloadLengthExtended32_1);
if ($array['a'] != 0 || ord(substr($payloadLengthExtended, 4, 1)) & 128) {
$this->wsSendClientClose($clientID, self::WS_STATUS_MESSAGE_TOO_BIG);
return false;
}
// fetch length as 32 bit unsigned integer, not as 64 bit
$payloadLengthExtended32_2 = substr($payloadLengthExtended, 4, 4);
$array = unpack('Na', $payloadLengthExtended32_2);
// check if the payload data length exceeds 2,147,479,538 (2,147,483,647 - 14 - 4095)
// 14 for header size, 4095 for last recv() next frame bytes
if ($array['a'] > 2147479538) {
$this->wsSendClientClose($clientID, self::WS_STATUS_MESSAGE_TOO_BIG);
return false;
}
// store frame payload data length
$this->wsClients[$clientID][7] = $array['a'];
}
}
// check if the frame's payload data length has now been stored
if ($this->wsClients[$clientID][7] !== false) {
// check if the frame's payload data length exceeds self::WS_MAX_FRAME_PAYLOAD_RECV
if ($this->wsClients[$clientID][7] > self::WS_MAX_FRAME_PAYLOAD_RECV) {
$this->wsClients[$clientID][7] = false;
$this->wsSendClientClose($clientID, self::WS_STATUS_MESSAGE_TOO_BIG);
return false;
}
// check if the message's payload data length exceeds 2,147,483,647 or self::WS_MAX_MESSAGE_PAYLOAD_RECV
// doesn't apply for control frames, where the payload data is not internally stored
$controlFrame = (ord(substr($this->wsClients[$clientID][9], 0, 1)) & 8) == 8;
if (!$controlFrame) {
$newMessagePayloadLength = $this->wsClients[$clientID][11] + $this->wsClients[$clientID][7];
if ($newMessagePayloadLength > self::WS_MAX_MESSAGE_PAYLOAD_RECV || $newMessagePayloadLength > 2147483647) {
$this->wsSendClientClose($clientID, self::WS_STATUS_MESSAGE_TOO_BIG);
return false;
}
}
return true;
}
}
return false;
}
function wsProcessClientFrame($clientID) {
// store the time that data was last received from the client
$this->wsClients[$clientID][3] = time();
// fetch frame buffer
$buffer = &$this->wsClients[$clientID][9];
// check at least 6 bytes are set (first 2 bytes and 4 bytes for the mask key)
if (substr($buffer, 5, 1) === false) return false;
// fetch first 2 bytes of header
$octet0 = ord(substr($buffer, 0, 1));
$octet1 = ord(substr($buffer, 1, 1));
$fin = $octet0 & self::WS_FIN;
$opcode = $octet0 & 15;
$mask = $octet1 & self::WS_MASK;
if (!$mask) return false; // close socket, as no mask bit was sent from the client
// fetch byte position where the mask key starts
$seek = $this->wsClients[$clientID][7] <= 125 ? 2 : ($this->wsClients[$clientID][7] <= 65535 ? 4 : 10);
// read mask key
$maskKey = substr($buffer, $seek, 4);
$array = unpack('Na', $maskKey);
$maskKey = $array['a'];
$maskKey = array(
$maskKey >> 24,
($maskKey >> 16) & 255,
($maskKey >> 8) & 255,
$maskKey & 255
);
$seek += 4;
// decode payload data
if (substr($buffer, $seek, 1) !== false) {
$data = str_split(substr($buffer, $seek));
foreach ($data as $key => $byte) {
$data[$key] = chr(ord($byte) ^ ($maskKey[$key % 4]));
}
$data = implode('', $data);
}
else {
$data = '';
}
// check if this is not a continuation frame and if there is already data in the message buffer
if ($opcode != self::WS_OPCODE_CONTINUATION && $this->wsClients[$clientID][11] > 0) {
// clear the message buffer
$this->wsClients[$clientID][11] = 0;
$this->wsClients[$clientID][1] = '';
}
// check if the frame is marked as the final frame in the message
if ($fin == self::WS_FIN) {
// check if this is the first frame in the message
if ($opcode != self::WS_OPCODE_CONTINUATION) {
// process the message
return $this->wsProcessClientMessage($clientID, $opcode, $data, $this->wsClients[$clientID][7]);
}
else {
// increase message payload data length
$this->wsClients[$clientID][11] += $this->wsClients[$clientID][7];
// push frame payload data onto message buffer
$this->wsClients[$clientID][1] .= $data;
// process the message
$result = $this->wsProcessClientMessage($clientID, $this->wsClients[$clientID][10], $this->wsClients[$clientID][1], $this->wsClients[$clientID][11]);
// check if the client wasn't removed, then reset message buffer and message opcode
if (isset($this->wsClients[$clientID])) {
$this->wsClients[$clientID][1] = '';
$this->wsClients[$clientID][10] = 0;
$this->wsClients[$clientID][11] = 0;
}
return $result;
}
}
else {
// check if the frame is a control frame, control frames cannot be fragmented
if ($opcode & 8) return false;
// increase message payload data length
$this->wsClients[$clientID][11] += $this->wsClients[$clientID][7];
// push frame payload data onto message buffer
$this->wsClients[$clientID][1] .= $data;
// if this is the first frame in the message, store the opcode
if ($opcode != self::WS_OPCODE_CONTINUATION) {
$this->wsClients[$clientID][10] = $opcode;
}
}
return true;
}
function wsProcessClientMessage($clientID, $opcode, &$data, $dataLength) {
// check opcodes
if ($opcode == self::WS_OPCODE_PING) {
// received ping message
return $this->wsSendClientMessage($clientID, self::WS_OPCODE_PONG, $data);
}
elseif ($opcode == self::WS_OPCODE_PONG) {
// received pong message (it's valid if the server did not send a ping request for this pong message)
if ($this->wsClients[$clientID][4] !== false) {
$this->wsClients[$clientID][4] = false;
}
}
elseif ($opcode == self::WS_OPCODE_CLOSE) {
// received close message
if (substr($data, 1, 1) !== false) {
$array = unpack('na', substr($data, 0, 2));
$status = $array['a'];
}
else {
$status = false;
}
if ($this->wsClients[$clientID][2] == self::WS_READY_STATE_CLOSING) {
// the server already sent a close frame to the client, this is the client's close frame reply
// (no need to send another close frame to the client)
$this->wsClients[$clientID][2] = self::WS_READY_STATE_CLOSED;
}
else {
// the server has not already sent a close frame to the client, send one now
$this->wsSendClientClose($clientID, self::WS_STATUS_NORMAL_CLOSE);
}
$this->wsRemoveClient($clientID);
}
elseif ($opcode == self::WS_OPCODE_TEXT || $opcode == self::WS_OPCODE_BINARY) {
if ( array_key_exists('message', $this->wsOnEvents) )
foreach ( $this->wsOnEvents['message'] as $func )
$func($clientID, $data, $dataLength, $opcode == self::WS_OPCODE_BINARY);
}
else {
// unknown opcode
return false;
}
return true;
}
function wsProcessClientHandshake($clientID, &$buffer) {
// fetch headers and request line
$sep = strpos($buffer, "\r\n\r\n");
if (!$sep) return false;
$headers = explode("\r\n", substr($buffer, 0, $sep));
$headersCount = sizeof($headers); // includes request line
if ($headersCount < 1) return false;
// fetch request and check it has at least 3 parts (space tokens)
$request = &$headers[0];
$requestParts = explode(' ', $request);
$requestPartsSize = sizeof($requestParts);
if ($requestPartsSize < 3) return false;
// check request method is GET
if (strtoupper($requestParts[0]) != 'GET') return false;
// check request HTTP version is at least 1.1
$httpPart = &$requestParts[$requestPartsSize - 1];
$httpParts = explode('/', $httpPart);
if (!isset($httpParts[1]) || (float) $httpParts[1] < 1.1) return false;
// store headers into a keyed array: array[headerKey] = headerValue
$headersKeyed = array();
for ($i=1; $i<$headersCount; $i++) {
$parts = explode(':', $headers[$i]);
if (!isset($parts[1])) return false;
$headersKeyed[trim($parts[0])] = trim($parts[1]);
}
// check Host header was received
if (!isset($headersKeyed['Host'])) return false;
// check Sec-WebSocket-Key header was received and decoded value length is 16
if (!isset($headersKeyed['Sec-WebSocket-Key'])) return false;
$key = $headersKeyed['Sec-WebSocket-Key'];
if (strlen(base64_decode($key)) != 16) return false;
// check Sec-WebSocket-Version header was received and value is 7
if (!isset($headersKeyed['Sec-WebSocket-Version']) || (int) $headersKeyed['Sec-WebSocket-Version'] < 7) return false; // should really be != 7, but Firefox 7 beta users send 8
// work out hash to use in Sec-WebSocket-Accept reply header
$hash = base64_encode(sha1($key.'258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
// build headers
$headers = array(
'HTTP/1.1 101 Switching Protocols',
'Upgrade: websocket',
'Connection: Upgrade',
'Sec-WebSocket-Accept: '.$hash
);
$headers = implode("\r\n", $headers)."\r\n\r\n";
// send headers back to client
$socket = $this->wsClients[$clientID][0];
$left = strlen($headers);
do {
$sent = #socket_send($socket, $headers, $left, 0);
if ($sent === false) return false;
$left -= $sent;
if ($sent > 0) $headers = substr($headers, $sent);
}
while ($left > 0);
return true;
}
// client write functions
function wsSendClientMessage($clientID, $opcode, $message) {
// check if client ready state is already closing or closed
if ($this->wsClients[$clientID][2] == self::WS_READY_STATE_CLOSING || $this->wsClients[$clientID][2] == self::WS_READY_STATE_CLOSED) return true;
// fetch message length
$messageLength = strlen($message);
// set max payload length per frame
$bufferSize = 4096;
// work out amount of frames to send, based on $bufferSize
$frameCount = ceil($messageLength / $bufferSize);
if ($frameCount == 0) $frameCount = 1;
// set last frame variables
$maxFrame = $frameCount - 1;
$lastFrameBufferLength = ($messageLength % $bufferSize) != 0 ? ($messageLength % $bufferSize) : ($messageLength != 0 ? $bufferSize : 0);
// loop around all frames to send
for ($i=0; $i<$frameCount; $i++) {
// fetch fin, opcode and buffer length for frame
$fin = $i != $maxFrame ? 0 : self::WS_FIN;
$opcode = $i != 0 ? self::WS_OPCODE_CONTINUATION : $opcode;
$bufferLength = $i != $maxFrame ? $bufferSize : $lastFrameBufferLength;
// set payload length variables for frame
if ($bufferLength <= 125) {
$payloadLength = $bufferLength;
$payloadLengthExtended = '';
$payloadLengthExtendedLength = 0;
}
elseif ($bufferLength <= 65535) {
$payloadLength = self::WS_PAYLOAD_LENGTH_16;
$payloadLengthExtended = pack('n', $bufferLength);
$payloadLengthExtendedLength = 2;
}
else {
$payloadLength = self::WS_PAYLOAD_LENGTH_63;
$payloadLengthExtended = pack('xxxxN', $bufferLength); // pack 32 bit int, should really be 64 bit int
$payloadLengthExtendedLength = 8;
}
// set frame bytes
$buffer = pack('n', (($fin | $opcode) << 8) | $payloadLength) . $payloadLengthExtended . substr($message, $i*$bufferSize, $bufferLength);
// send frame
$socket = $this->wsClients[$clientID][0];
$left = 2 + $payloadLengthExtendedLength + $bufferLength;
do {
$sent = #socket_send($socket, $buffer, $left, 0);
if ($sent === false) return false;
$left -= $sent;
if ($sent > 0) $buffer = substr($buffer, $sent);
}
while ($left > 0);
}
return true;
}
function wsSendClientClose($clientID, $status=false) {
// check if client ready state is already closing or closed
if ($this->wsClients[$clientID][2] == self::WS_READY_STATE_CLOSING || $this->wsClients[$clientID][2] == self::WS_READY_STATE_CLOSED) return true;
// store close status
$this->wsClients[$clientID][5] = $status;
// send close frame to client
$status = $status !== false ? pack('n', $status) : '';
$this->wsSendClientMessage($clientID, self::WS_OPCODE_CLOSE, $status);
// set client ready state to closing
$this->wsClients[$clientID][2] = self::WS_READY_STATE_CLOSING;
}
// client non-internal functions
function wsClose($clientID) {
return $this->wsSendClientClose($clientID, self::WS_STATUS_NORMAL_CLOSE);
}
function wsSend($clientID, $message, $binary=false) {
return $this->wsSendClientMessage($clientID, $binary ? self::WS_OPCODE_BINARY : self::WS_OPCODE_TEXT, $message);
}
function log( $message )
{
echo date('Y-m-d H:i:s: ') . $message . "\n";
}
function bind( $type, $func )
{
if ( !isset($this->wsOnEvents[$type]) )
$this->wsOnEvents[$type] = array();
$this->wsOnEvents[$type][] = $func;
}
function unbind( $type='' )
{
if ( $type ) unset($this->wsOnEvents[$type]);
else $this->wsOnEvents = array();
}
}
?>
Related
How to generate unique QR codes with Google Authenticator in PHP?
want for each of the user on our PHP system to have a unique QR code. I have implemented the Google Authenticator system but it only generates the same QR code for each user and one user can use their code to log-in to another user's account. How do I get Google Authenticator to generate a unique code for each user? What value/variable do I need to pass onto Google Authenticator for it to come up with a unique code for each user account on our system? We have tried passing the email address or username of the user but the Authenticator sends back the same QR code despite the email addresses being unique. [1]using username , username variable name is $name public function getQR($name, $secret, $title = null, $params = array()) { $width = !empty($params['width']) && (int) $params['width'] > 0 ? (int) $params['width'] : 200; $height = !empty($params['height']) && (int) $params['height'] > 0 ? (int) $params['height'] : 200; $level = !empty($params['level']) && array_search($params['level'], array('L', 'M', 'Q', 'H')) !== false ? $params['level'] : 'M'; $urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.''); if (isset($title)) { $urlencoded .= urlencode('&issuer='.urlencode($title)); } return 'https://chart.googleapis.com/chart?chs='.$width.'x'.$height.'&chld='.$level.'|0&cht=qr&chl='.$urlencoded.''; } public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null) { if ($currentTimeSlice === null) { $currentTimeSlice = floor(time() / 30); } if (strlen($code) != 6) { return false; } for ($i = -$discrepancy; $i <= $discrepancy; ++$i) { $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i); if ($this->timingSafeEquals($calculatedCode, $code)) { return true; } } return false; } [2]using email , email variable name is $email public function getQR($email, $secret, $title = null, $params = array()) { $width = !empty($params['width']) && (int) $params['width'] > 0 ? (int) $params['width'] : 200; $height = !empty($params['height']) && (int) $params['height'] > 0 ? (int) $params['height'] : 200; $level = !empty($params['level']) && array_search($params['level'], array('L', 'M', 'Q', 'H')) !== false ? $params['level'] : 'M'; $urlencoded = urlencode('otpauth://totp/'.$email.'?secret='.$secret.''); if (isset($title)) { $urlencoded .= urlencode('&issuer='.urlencode($title)); } return 'https://chart.googleapis.com/chart?chs='.$width.'x'.$height.'&chld='.$level.'|0&cht=qr&chl='.$urlencoded.''; } public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null) { if ($currentTimeSlice === null) { $currentTimeSlice = floor(time() / 30); } if (strlen($code) != 6) { return false; } for ($i = -$discrepancy; $i <= $discrepancy; ++$i) { $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i); if ($this->timingSafeEquals($calculatedCode, $code)) { return true; } } return false; } I want for the code to generate a unique QR code for each user so each user have their own authentication codes in the Google Authenticator app on their phones.
The secret is nothing more than a random base32 string. So can be done as simple as substr( str_shuffle( "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" ), 0, 32 ); Where "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" is the base32 alphabet
Your code looks pretty similar to https://github.com/PHPGangsta/GoogleAuthenticator and problem that leads to similar QR codes for different users is that you are using same secret for they. Every user must have unique secret! There is function by PHPGangsta that generates pseudorandom secret: public function createSecret($secretLength = 16) { $validChars = $this->_getBase32LookupTable(); // Valid secret lengths are 80 to 640 bits if ($secretLength < 16 || $secretLength > 128) { throw new Exception('Bad secret length'); } $secret = ''; $rnd = false; if (function_exists('random_bytes')) { $rnd = random_bytes($secretLength); } elseif (function_exists('mcrypt_create_iv')) { $rnd = mcrypt_create_iv($secretLength, MCRYPT_DEV_URANDOM); } elseif (function_exists('openssl_random_pseudo_bytes')) { $rnd = openssl_random_pseudo_bytes($secretLength, $cryptoStrong); if (!$cryptoStrong) { $rnd = false; } } if ($rnd !== false) { for ($i = 0; $i < $secretLength; ++$i) { $secret .= $validChars[ord($rnd[$i]) & 31]; } } else { throw new Exception('No source of secure random'); } return $secret; }
Allowing multiple connection for the clients
I am having problem i could not connect all my client in my socket.it only connect one client at a time when a new connection made.how can i make all my clients send data together or connect them all.I hope someone can help me on this. Thank you in advance. // Set time limit to indefinite execution set_time_limit (0); // Set the ip and port we will listen on $address = '192.168.0.11';//server ip demo only. $port = 123; $max_clients = 10; // Array that will hold client information $client = array(); // Create a TCP Stream socket $sock = socket_create(AF_INET, SOCK_STREAM, 0); // Bind the socket to an address/port socket_bind($sock, $address, $port) or die('Could not bind to address'); // Start listening for connections socket_listen($sock); // Loop continuously while (true) { $file = fopen('txt.log','a'); // Setup clients listen socket for reading $read[0] = $sock; for ($i = 0; $i < $max_clients; $i++) { if (isset($client[$i])) if ($client[$i]['sock'] != null) $read[$i + 1] = $client[$i]['sock'] ; } // Set up a blocking call to socket_select() $write = null; $except = NULL; $tv_sec = NULL; echo "connecting"; $ready = socket_select($read,$write,$except,$tv_sec); /* if a new connection is being made add it to the client array */ if (in_array($sock, $read)) { for ($i = 0; $i < $max_clients; $i++) { if (!isset($client[$i])) { $client[$i] = array(); socket_set_nonblock($sock); $client[$i]['sock'] = socket_accept($sock); $file = fopen('txt.log','a'); echo("Accepting incomming connection...\n"); break; } elseif ($i == $max_clients - 1) print ("too many clients"); } if (--$ready <= 0) continue; } // end if in_array // If a client is trying to write - handle it now for ($i = 0; $i < $max_clients; $i++) // for each client { if (isset($client[$i])) if (in_array($client[$i]['sock'] , $read)) { $input = socket_read($client[$i]['sock'] , 1024); if ($input == null) { // Zero length string meaning disconnected echo("Client disconnected\n"); unset($client[$i]); fclose($file); } $n = trim($input); if ($n == 'exit') { echo("Client requested disconnect\n"); // requested disconnect socket_close($client[$i]['sock']); } elseif ($input) { echo("Receiving data\n"); // strip white spaces and write back to user $output = ($input); //socket_write($client[$i]['sock'],$output); fwrite($file,$output); echo $output."\r\n"; } } else { // Close the socket if (isset($client[$i])) echo("Client disconnected\n"); if ($client[$i]['sock'] != null){ socket_close($client[$i]['sock']); unset($client[$i]); } } } fclose($file);//closing file } // end while // Close the master sockets echo("Shutting down\n"); socket_close($sock); fclose($file);
Why does this code cause high load average on the server?
The code below sends a msg when a value, obtained from a not so stable API, is within a certain range. This code causes the load average, but not the CPU usage, to go up, likely due to high I/O wait. CentOS 6.5 Kernel 2.6.32-431.11.2.el6.x86_64 Apache 2.2.15 PHP 5.3.3 (mod_fcgid/2.3.7) > cat /sys/block/sda/queue/scheduler noop anticipatory deadline [cfq] The code had the same problematic effect on both dedicated hardware and on the cloud (both cases as a VM on KVM/Virtio). What could be done to keep this code from *causing the processor to wait on instruction completion before processing new instructions**? I understand that lowering the timeout doesn't really solve the problem, only diminishes its impact. *this is my understanding why this code causes load average to go up. <?php $min = '1'; $last_min = '1'; if (!empty($_GET['min'])) { $min = $_GET['min']; } else { $min = false; } $ctx=stream_context_create(array('http'=> array( 'timeout' => 10 // seconds timeout ) )); $json = file_get_contents('https://www.domain.com/api/ticker/',false,$ctx); if (!empty($json )) { echo($json); if (#file_get_contents('log.txt')) { if (quote_changed($json)) { file_put_contents('log.txt', $json, FILE_APPEND); } } else { file_put_contents('log.txt', $json); } $obj = json_decode($json,true); $last = $obj['ticker']['last']; if (is_numeric($last)) { $last = (int)$last; $last_min = #file_get_contents('last_min.txt'); $notified = #file_get_contents('notified.txt'); if ($notified === false) { $notified = 'false'; #echo "no notify file\n"; } if (($last_min === false) || (($min) && ($last_min <> $min))) { $last_min = 1; $notified = 'false'; file_put_contents('last_min.txt', $min); #echo "no min file or diff min\n"; } #echo ('notified='.$notified.'\n'); if (($last >= $min) && ($notified=='false')) { #$url = ('http://otherdomain.com/nexmo/sendmsg.php' . '?name=blah' . $last); #file_get_contents($url); #switch to SMS when going abroad and plugin new number when available mail("8885551212#mail.net","Blah at".$last,"","From: gaia#domain.com\n"); file_put_contents('notified.txt', 'true'); #echo "msg sent\n"; } elseif (($last < $min) && ($notified=='true')) { file_put_contents('notified.txt', 'false'); #echo "not sent\n"; } } } function quote_changed($current) { $previous = tailCustom('log.txt'); #echo ('previous='.$previous); if ($previous === (trim($current))) { return 0; } else { return 1; } } function tailCustom($filepath, $lines = 1, $adaptive = true) { // Open file $f = #fopen($filepath, "rb"); if ($f === false) return false; // Sets buffer size if (!$adaptive) $buffer = 4096; else $buffer = ($lines < 2 ? 64 : ($lines < 10 ? 512 : 4096)); // Jump to last character fseek($f, -1, SEEK_END); // Read it and adjust line number if necessary // (Otherwise the result would be wrong if file doesn't end with a blank line) if (fread($f, 1) != "\n") $lines -= 1; // Start reading $output = ''; $chunk = ''; // While we would like more while (ftell($f) > 0 && $lines >= 0) { // Figure out how far back we should jump $seek = min(ftell($f), $buffer); // Do the jump (backwards, relative to where we are) fseek($f, -$seek, SEEK_CUR); // Read a chunk and prepend it to our output $output = ($chunk = fread($f, $seek)) . $output; // Jump back to where we started reading fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR); // Decrease our line counter $lines -= substr_count($chunk, "\n"); } // While we have too many lines // (Because of buffer size we might have read too many) while ($lines++ < 0) { // Find first newline and remove all text before that $output = substr($output, strpos($output, "\n") + 1); } // Close file and return fclose($f); return trim($output); } ?>
Move the data into a database or if that's not an option, cache the unstable file into your server locally so you don't have to hit the external provider every time. If neither of those are an option, it seems to me that the people providing the API need to improve their performance, you can verify this by just benchmarking how many milliseconds each hit takes through Firebug.
Decompressing a .gz file via PHP
I need to be able to decompress through PHP some data that I have in a string which uses the gzip format. I need to do this via PHP, not by calling - through system for example - an external program. I go to the documentation and I find gzdecode. Too bad it doesn't exist. Digging further through google it appears this function was implemented in PHP6, which I cannot use. (Interestingly enough gzencode exists and is working). I believe - but I'm not sure - that the gzip format simply has some extra header data. Is there a way to uncompress it by manipulating this extra data and then using gzuncompress, or some other way? Thanks
gzdecode() is not yet in PHP. But you can use the implementation from upgradephp. It really is just a few extra header bytes. Another option would be to use gzopen. Maybe just like gzopen("data:app/bin,....") even.
Well I found my answer by reading the comments on the gzdecode page I linked in my original post. One of the users, Aaron G, provided an implementation of it and it works: <?php function gzdecode($data) { $len = strlen($data); if ($len < 18 || strcmp(substr($data,0,2),"\x1f\x8b")) { return null; // Not GZIP format (See RFC 1952) } $method = ord(substr($data,2,1)); // Compression method $flags = ord(substr($data,3,1)); // Flags if ($flags & 31 != $flags) { // Reserved bits are set -- NOT ALLOWED by RFC 1952 return null; } // NOTE: $mtime may be negative (PHP integer limitations) $mtime = unpack("V", substr($data,4,4)); $mtime = $mtime[1]; $xfl = substr($data,8,1); $os = substr($data,8,1); $headerlen = 10; $extralen = 0; $extra = ""; if ($flags & 4) { // 2-byte length prefixed EXTRA data in header if ($len - $headerlen - 2 < 8) { return false; // Invalid format } $extralen = unpack("v",substr($data,8,2)); $extralen = $extralen[1]; if ($len - $headerlen - 2 - $extralen < 8) { return false; // Invalid format } $extra = substr($data,10,$extralen); $headerlen += 2 + $extralen; } $filenamelen = 0; $filename = ""; if ($flags & 8) { // C-style string file NAME data in header if ($len - $headerlen - 1 < 8) { return false; // Invalid format } $filenamelen = strpos(substr($data,8+$extralen),chr(0)); if ($filenamelen === false || $len - $headerlen - $filenamelen - 1 < 8) { return false; // Invalid format } $filename = substr($data,$headerlen,$filenamelen); $headerlen += $filenamelen + 1; } $commentlen = 0; $comment = ""; if ($flags & 16) { // C-style string COMMENT data in header if ($len - $headerlen - 1 < 8) { return false; // Invalid format } $commentlen = strpos(substr($data,8+$extralen+$filenamelen),chr(0)); if ($commentlen === false || $len - $headerlen - $commentlen - 1 < 8) { return false; // Invalid header format } $comment = substr($data,$headerlen,$commentlen); $headerlen += $commentlen + 1; } $headercrc = ""; if ($flags & 1) { // 2-bytes (lowest order) of CRC32 on header present if ($len - $headerlen - 2 < 8) { return false; // Invalid format } $calccrc = crc32(substr($data,0,$headerlen)) & 0xffff; $headercrc = unpack("v", substr($data,$headerlen,2)); $headercrc = $headercrc[1]; if ($headercrc != $calccrc) { return false; // Bad header CRC } $headerlen += 2; } // GZIP FOOTER - These be negative due to PHP's limitations $datacrc = unpack("V",substr($data,-8,4)); $datacrc = $datacrc[1]; $isize = unpack("V",substr($data,-4)); $isize = $isize[1]; // Perform the decompression: $bodylen = $len-$headerlen-8; if ($bodylen < 1) { // This should never happen - IMPLEMENTATION BUG! return null; } $body = substr($data,$headerlen,$bodylen); $data = ""; if ($bodylen > 0) { switch ($method) { case 8: // Currently the only supported compression method: $data = gzinflate($body); break; default: // Unknown compression method return false; } } else { // I'm not sure if zero-byte body content is allowed. // Allow it for now... Do nothing... } // Verifiy decompressed size and CRC32: // NOTE: This may fail with large data sizes depending on how // PHP's integer limitations affect strlen() since $isize // may be negative for large sizes. if ($isize != strlen($data) || crc32($data) != $datacrc) { // Bad format! Length or CRC doesn't match! return false; } return $data; } ?>
Try gzinflate.
Did you tried gzuncompress? http://www.php.net/manual/en/function.gzuncompress.php
Restricting access to a site using IP address
I would like to know whether there is a way of restricting the users of a site such that they can only access the inner pages of a site if they are within a certain range of IP addresses or a certain network? The current PHP scripts I am getting cant differentiate the real IPs from the Proxies? Thanks
i wouldn’t restrict on ip addresses. as you said, you can’t know if it’s a proxy. furthermore, ip addresses can be easily spoofed.
Have you considered using apache .htaccess files for that? IP restriction with htaccess
You can try out a script I created that allows very advanced IP rules. I coded it years ago so I apologize in advance for the current shape of it. Edit: If you're looking for an "&" operator in the syntax don't bother. I forgot to add it when I coded this and looking back at this script now makes me cringe at the thought of touching it again. <?php ############################################################## # IP Expression Class # # Easy IP-based Access Restrictions # # Change Log: # # - Added Range and limited IPv6 support # # - Changed name from IPAR to IPEX # # # ############################################################## # Example Rules: # # 69.[10-20].[^50].* # # 69.*.[1-5 | 10-20 |^30].* # # 60.12.2.* # # 127.* # # 69.1.1.1-70.1.1.1 <-- This is a range # # # # Usage: # # Ipex::IsMatch($rule, $ip); # # # # [range] - Defines a range for a section of the IP # # | - OR token. IP can match this range/number # # ^ - NOT token. IP can not match this range/number # # x-y - Defines a range from x to y # # x - Exactly match x (x = a hex or dec number) # # * - Match any number # # # #----------===============================-------------------# # [ Written by Chris Tarquini ] # #----------===============================-------------------# ############################################################## define('IPR_DENY', false); define('IPR_ALLOW', true); define('IPR_ERR_MISMATCH',-1); define('IPR_ERR_RANGE_MISMATCH',-2); define('IPR_ERR_RANGE_INVALID',-3); define('IPR_ERR_INVALID_RULE',-4); class IPEX { const TOKEN_RANGE_BEGIN = '['; const TOKEN_RANGE_END = ']'; const TOKEN_WILDCARD = '*'; const TOKEN_RANGE_SPLIT = '-'; const TOKEN_OR = '|'; const TOKEN_NOT = '^'; const DEBUG_MODE = TRUE; private static function trace($err){if(self::DEBUG_MODE) echo "$err\r\n";} private static function FixRule($rule,$count = 4, $split='.') { $rule = explode($split,$rule); $filler = 0; $size = sizeof($rule); for($i = 0; $i < $count; $i++) { if($i > $size) { $rule[] = $filler; $size++;} else if(empty($rule[$i])) { $filler = self::TOKEN_WILDCARD; $rule[$i] = $filler;} } return $rule; } private static function FixIP($rule,$count = 4, $split='.') { $rule = explode($split,$rule); $size = sizeof($rule); for($i = 0; $i < $count; $i++) { if($i > $size) { $rule[] = 0; $size++;} else if(empty($rule[$i])) { $rule[$i] = 0;} } return $rule; } private static function GetIpType(&$ip) { $mode = IPID::Identify($ip,$newip); if($mode == IPID_IPv4_Embed) { $ip = $newip; return IPID_IPv4;} return $mode; } private static function FixIPRange(&$start, &$stop) { $count = 4; $split = '.'; if(self::GetIpType($start) == IPID_IPv6) {$count = 8; $split = ':';} $q = 0; while($q < 2) { $filler = ($q == 0) ? 0 : 255; $arr = explode($split,($q == 0) ? $start : $stop); $size = sizeof($arr); for($i = 0; $i < $count; $i++) { if($i > $size){ $arr[] = $filler; $size++;} else if(empty($arr[$i])){ $arr[$i] = $filler; } } if($q == 0) $start = implode($split, $arr); else $stop = implode($split,$arr); $q++; } } public static function IsInRange($start, $stop, $ip) { //Sorry guys we only support IPv4 for this ;( self::FixIPRange($start,$stop); self::trace("fixed: start = $start, stop = $stop"); $start = ip2long($start); $stop = ip2long($stop); $ip = ip2long($ip); self::trace("start = $start, stop = $stop, ip = $ip"); return ($ip >= $start && $ip <= $stop); } public static function IsAllowed($rule, $ip){return self::IsMatch($rule,$ip);} public static function IsMatch($rule,$ip) { $mode = self::GetIpType($ip); self::trace("ip type: $mode"); if(strpos($rule, self::TOKEN_RANGE_SPLIT) !== false && strpos($rule,self::TOKEN_RANGE_BEGIN) === false) { self::trace("ip range mode"); $test = explode(self::TOKEN_RANGE_SPLIT, $rule); self::trace("range size: ".sizeof($test)); print_r($test); if(sizeof($test) != 2) return IPR_ERR_RANGE_INVALID; $start = $test[0]; $end = $test[1]; if(empty($start) || empty($end)) return IPR_ERR_RANGE_INVALID; self::trace("range start: $start, range stop: $end"); $rm1 = (self::IsHex($start)) ? $mode : self::GetIpType($start); $rm2 = (self::IsHex($end)) ? $mode : self::GetIpType($end); self::trace("range types: $rm1, $rm2\r\nip type: $mode"); if($rm1 != $rm2 || $rm1 != $mode) return IPR_ERR_RANGE_MISMATCH; if($mode == IPID_IPv6) { return IPR_ERR_IPv6_NOTSUPPORTED;} return self::IsInRange($start,$end,$ip); } if(self::GetIpType($rule) != $mode) return IPR_ERR_MISMATCH; //all is good so far $count = 4; $split = '.'; if($mode==IPID_IPv6){$count = 8; $split=':';} $rule = self::FixRule($rule, $count,$split); $ip = self::FixIp($ip,$count,$split); self::trace("ip: ".implode($split,$ip)); self::trace('rule: '.implode($split,$rule)); for($i = 0; $i < $count; $i++) { $r = str_replace(' ', '', $rule[$i]); $ri = false; if($r == self::TOKEN_WILDCARD) continue; if($mode == IPPID_IPv6 && self::IsHex($r)) { $ri = hexdec($r);}else if(is_numeric($r)) $ri = $r; $x = $ip[$i]; if($mode == IPPID_IPv6) $x = hexdec($x); //* Exact Match *// self::trace("rule[$i]: $ri"); self::trace("ip[$i]: $x"); if($ri !== false && $ri != $x) return IPR_DENY; $len = strlen($r); for($y = 0; $y < $len; $y++) { self::trace("y = $y"); if(substr($r, $y,1) == self::TOKEN_RANGE_BEGIN) { ++$y; self::trace("found range, y = $y"); $negflag = false; $start = false; $stop = false; $allows = 0; $denys = 0; $q = 0; $c = substr($r,$y,1); while($c !== false) { self::trace("in range, char: $c"); //* Flags *// $break = false; $exec = false; $toggle = false; $reset = false; if($c === self::TOKEN_RANGE_END) {$skiphex = true;$break = true; $exec = true; self::trace("found end of range");} if($c === self::TOKEN_NOT) {if($q > 0){ $toggle = true; $exec = true;} else $negflag = !$negflag; $skiphex =false; self::trace("found TOKEN_NOT");} if($c === self::TOKEN_OR) { $exec = true; $reset = true;$skiphex=true;self::trace("found TOKEN_OR");} if($c === self::TOKEN_RANGE_SPLIT){ $skiphex = false;++$q; self::trace("found range split");} //* Read Hex Tokens *// if(!$skiphex && self::IsHexChar($c)) { $n = self::ReadNextHexToken($r,$y); if($mode == IPID_IPv6) $n = hexdec($n); if($q == 0) $start = $n; else if($q == 1) $stop = $n; --$y; //fixes error self::trace("parsed number: $n, y = $y"); } if($reset) {$negflag = false; $start = false; $stop = false; $q = 0;} if($exec) { self::trace("executing: start = $start, stop = $stop, x = $x"); self::trace("negflag = $negflag"); if($stop !== false && $x >= $start && $x <= $stop) { if($negflag) { ++$denys; $allows = 0; break;} else ++$allows; } else if($stop === false && $start == $x) { if($negflag) { ++$denys; $allows = 0; break;} else ++$allows; } self::trace("exec complete: allows = $allows, denys = $denys"); $q = 0; } if($toggle) $negflag = !$negflag; if($break) break; ++$y; $c = substr($r,$y,1); } if(!$allows) return IPR_DENY; } } } return IPR_ALLOW; } private static function ReadNextHexToken($buff, &$offset, $max = -1) { $str = ''; if($max == -1) { $max = strlen($buff);} for(; $offset < $max; $offset++) { $c = substr($buff,$offset, 1); if(self::IsHexChar($c)) $str .= $c; else return $str; } return $str; } private static function IsHex($x){ $len = strlen($x); for($i = 0; $i < $len; $i++) if(!self::IsHexChar(substr($x,$i,1))) return false; return true;} private static function IsHexChar($x){self::trace("isHex($x);"); return (in_array(strtoupper($x),array('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'))); } } ###################### # IP Identify Class # ##################### define('IPID_INVALID',false); define('IPID_IPv4',2); define('IPID_IPv6',3); define('IPID_IPv4_Embed',6); class IPID { public static function Identify($ip,&$ipconvert = false) { $ip = strtoupper($ip); $ipconvert = $ip; // Check if we are IPv4 if(strpos($ip,':') === false && strpos($ip,'.') !== false) return IPID_IPv4; //Is it one of those hybrids? else if(strpos($ip,':FFFF') !== false && strpos($ip,'.') !== false) { $ipconvert = substr($ip,strpos($ip,':FFFF:')+6); return IPID_IPv4_Embed; } // Is it IPv6? else if(strpos($ip,':') !== false) return IPID_IPv6; // What the...? return IPID_INVALID; } } ?> You can use it as long as you don't try and resell it and you keep the header as is.
<?php //This function returns True if visitor IP is allowed. //Otherwise it returns False. function CheckAccess() { //allowed IP. Change it to your static IP $allowedip = '127.0.0.1'; $ip = $_SERVER['REMOTE_ADDR']; return ($ip == $allowedip); }
Proxy servers should set the X-Forwarded-For HTTP header, which you could look up with $_SERVER['HTTP_X_FORWARDED_FOR']. Otherwise $_SERVER['REMOTE_ADDR'] can be used to get the IP address. As others have noted, both of these can be easily spoofed, and there is no requirement for proxies to set the X-Forwarded-For request header. There is a ip2long() function in PHP which give you an integer to use for range checking. To get the location of an IP address you need a lookup table which maps IP address ranges to approximate geographical locations (such lookup tables are typically not free). There are many services which offer IP address geolocation, some of which are mentioned here and here.