Author: Vasily Zorin (maintainer@daemon.io)
Inheritance: use trait PHPDaemon\Traits\ClassWatchdog, use trait PHPDaemon\Traits\StaticObjectWatchdog
Esempio n. 1
0
 /**
  * Called when new data received
  * @return void
  */
 public function onRead()
 {
     $packet = $this->read(1024);
     $orig = $packet;
     $type = Binary::getByte($packet);
     $code = Binary::getByte($packet);
     $checksum = Binary::getStrWord($packet);
     $id = Binary::getWord($packet);
     $seq = Binary::getWord($packet);
     if ($checksum !== self::checksum(substr_replace($orig, "", 2, 2))) {
         $status = 'badChecksum';
     } elseif ($type === 0x3) {
         $status = isset(static::$unreachableCodes[$code]) ? static::$unreachableCodes[$code] : 'unk' . $code . 'unreachable';
     } else {
         $status = 'unknownType0x' . dechex($type);
     }
     while (!$this->onResponse->isEmpty()) {
         $el = $this->onResponse->shift();
         if ($el instanceof CallbackWrapper) {
             $el = $el->unwrap();
         }
         list($cb, $st) = $el;
         call_user_func($cb, microtime(true) - $st, $status);
     }
     $this->finish();
 }
Esempio n. 2
0
 /**
  * Parse structure of labels
  * @param  string &$data Binary data
  * @param  string $orig  Original packet
  * @return string        Dot-separated labels list
  */
 public static function parseLabels(&$data, $orig = null)
 {
     $str = '';
     while (strlen($data) > 0) {
         $l = ord($data[0]);
         if ($l >= 192) {
             $pos = Binary::bytes2int(chr($l - 192) . binarySubstr($data, 1, 1));
             $data = binarySubstr($data, 2);
             $ref = binarySubstr($orig, $pos);
             return $str . Binary::parseLabels($ref);
         }
         $p = substr($data, 1, $l);
         $str .= $p . ($l !== 0 ? '.' : '');
         $data = substr($data, $l + 1);
         if ($l === 0) {
             break;
         }
     }
     return $str;
 }
Esempio n. 3
0
 /**
  * Called when new data received
  * @see http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10#page-16
  * @return void
  */
 public function onRead()
 {
     if ($this->state === self::STATE_PREHANDSHAKE) {
         if (!$this->handshake()) {
             return;
         }
     }
     if ($this->state === self::STATE_HANDSHAKED) {
         while (($buflen = $this->getInputLength()) >= 2) {
             $first = ord($this->look(1));
             // first byte integer (fin, opcode)
             $firstBits = decbin($first);
             $opcode = (int) bindec(substr($firstBits, 4, 4));
             if ($opcode === 0x8) {
                 // CLOSE
                 $this->finish();
                 return;
             }
             $opcodeName = isset(static::$opcodes[$opcode]) ? static::$opcodes[$opcode] : false;
             if (!$opcodeName) {
                 Daemon::log(get_class($this) . ': Undefined opcode ' . $opcode);
                 $this->finish();
                 return;
             }
             $second = ord($this->look(1, 1));
             // second byte integer (masked, payload length)
             $fin = (bool) ($first >> 7);
             $isMasked = (bool) ($second >> 7);
             $dataLength = $second & 0x7f;
             $p = 2;
             if ($dataLength === 0x7e) {
                 // 2 bytes-length
                 if ($buflen < $p + 2) {
                     return;
                     // not enough data yet
                 }
                 $dataLength = Binary::bytes2int($this->look(2, $p), false);
                 $p += 2;
             } elseif ($dataLength === 0x7f) {
                 // 8 bytes-length
                 if ($buflen < $p + 8) {
                     return;
                     // not enough data yet
                 }
                 $dataLength = Binary::bytes2int($this->look(8, $p));
                 $p += 8;
             }
             if ($this->pool->maxAllowedPacket <= $dataLength) {
                 // Too big packet
                 $this->finish();
                 return;
             }
             if ($isMasked) {
                 if ($buflen < $p + 4) {
                     return;
                     // not enough data yet
                 }
                 $mask = $this->look(4, $p);
                 $p += 4;
             }
             if ($buflen < $p + $dataLength) {
                 return;
                 // not enough data yet
             }
             $this->drain($p);
             $data = $this->read($dataLength);
             if ($isMasked) {
                 $data = $this->mask($data, $mask);
             }
             //Daemon::log(Debug::dump(array('ext' => $this->extensions, 'rsv1' => $firstBits[1], 'data' => Debug::exportBytes($data))));
             /*if ($firstBits[1] && in_array('deflate-frame', $this->extensions)) { // deflate frame
                   $data = gzuncompress($data, $this->pool->maxAllowedPacket);
               }*/
             if (!$fin) {
                 $this->framebuf .= $data;
             } else {
                 $this->onFrame($this->framebuf . $data, $opcodeName);
                 $this->framebuf = '';
             }
         }
     }
 }
Esempio n. 4
0
 /**
  * Called when new data received
  * @return void
  */
 public function onRead()
 {
     start:
     if ($this->state === static::STATE_HEADER) {
         $l = $this->getInputLength();
         if ($l < 2) {
             return;
         }
         $hdr = $this->look(2);
         $fb = Binary::getbitmap(ord($hdr));
         $fin = (bool) $fb[0];
         $opCode = bindec(substr($fb, 4, 4));
         if (isset($this->opCodes[$opCode])) {
             $this->type = $this->opCodes[$opCode];
         } else {
             $this->log('opCode: ' . $opCode . ': unknown frame type');
             $this->finish();
             return;
         }
         $sb = ord(binarySubstr($hdr, 1));
         $sbm = Binary::getbitmap($sb);
         $this->isMasked = (bool) $sbm[0];
         $payloadLength = $sb & 127;
         if ($payloadLength <= 125) {
             $this->drain(2);
             $this->pctLength = $payloadLength;
         } elseif ($payloadLength === 126) {
             if ($l < 4) {
                 return;
             }
             $this->drain(2);
             $this->pctLength = Binary::b2i($this->read(2));
         } elseif ($payloadLength === 127) {
             if ($l < 10) {
                 return;
             }
             $this->drain(2);
             $this->pctLength = Binary::b2i($this->read(8));
         }
         if ($this->pool->maxAllowedPacket < $this->pctLength) {
             Daemon::$process->log('max-allowed-packet (' . $this->pool->config->maxallowedpacket->getHumanValue() . ') exceed, aborting connection');
             $this->finish();
             return;
         }
         $this->setWatermark($this->pctLength + ($this->isMasked ? 4 : 0));
         $this->state = static::STATE_DATA;
     }
     if ($this->state === static::STATE_DATA) {
         if ($this->getInputLength() < $this->pctLength + ($this->isMasked ? 4 : 0)) {
             return;
         }
         $this->state = static::STATE_HEADER;
         $this->setWatermark(2);
         if ($this->isMasked) {
             $this->trigger('frame', static::mask($this->read(4), $this->read($this->pctLength)));
         } else {
             $this->trigger('frame', $this->read($this->pctLength));
         }
     }
     if ($this->state == static::STATE_STANDBY) {
         while (($line = $this->readLine()) !== null) {
             $line = trim($line);
             if ($line == '') {
                 $expectedKey = base64_encode(pack('H*', sha1($this->key . static::GUID)));
                 if (isset($this->headers['HTTP_SEC_WEBSOCKET_ACCEPT']) && $expectedKey == $this->headers['HTTP_SEC_WEBSOCKET_ACCEPT']) {
                     $this->state = static::STATE_HEADER;
                     if ($this->onConnected) {
                         $this->connected = true;
                         $this->onConnected->executeAll($this);
                         $this->onConnected = null;
                     }
                     $this->trigger('connected');
                     goto start;
                 } else {
                     Daemon::$process->log(__METHOD__ . ': Handshake failed. Connection to ' . $this->url . ' failed.');
                     $this->finish();
                 }
             } else {
                 $e = explode(': ', $line);
                 if (isset($e[1])) {
                     $this->headers['HTTP_' . strtoupper(strtr($e[0], ['-' => '_']))] = $e[1];
                 }
             }
         }
         return;
     }
     goto start;
 }
Esempio n. 5
0
 /**
  * Gets the host information
  * @param  string   $hostname Hostname
  * @param  callable $cb       Callback
  * @callback $cb ( )
  * @return void
  */
 public function get($hostname, $cb)
 {
     $this->onResponse->push($cb);
     $this->setFree(false);
     $e = explode(':', $hostname, 3);
     $hostname = $e[0];
     $qtype = isset($e[1]) ? $e[1] : 'A';
     $qclass = isset($e[2]) ? $e[2] : 'IN';
     $QD = [];
     $qtypeInt = array_search($qtype, Pool::$type, true);
     $qclassInt = array_search($qclass, Pool::$class, true);
     if ($qtypeInt === false || $qclassInt === false) {
         call_user_func($cb, false);
         return;
     }
     $q = Binary::labels($hostname) . Binary::word($qtypeInt) . Binary::word($qclassInt);
     $QD[] = $q;
     $packet = Binary::word(++$this->seq) . Binary::bitmap2bytes('0' . '0000' . '0' . '0' . '1' . '0' . '000' . '0000', 2) . Binary::word(sizeof($QD)) . Binary::word(0) . Binary::word(0) . Binary::word(0) . implode('', $QD);
     if ($this->type === 'udp') {
         $this->write($packet);
     } else {
         $this->write(Binary::word(strlen($packet)) . $packet);
     }
 }
Esempio n. 6
0
 /**
  * onRead
  * @return void
  */
 protected function onRead()
 {
     start:
     if ($this->state === static::STATE_STANDBY) {
         if (($hdr = $this->readExact(2)) === false) {
             return;
             // not enough data
         }
         $u = unpack('S', $hdr);
         $this->responseCode = $u[1];
         $this->state = static::STATE_PACKET_HDR;
     }
     if ($this->state === static::STATE_PACKET_HDR) {
         if ($this->responseCode === static::REPL_KVAL) {
             $this->result = [];
             if (($hdr = $this->readExact(9)) === false) {
                 return;
                 // not enough data
             }
             $this->encoding = Binary::getByte($hdr);
             $this->responseLength = Binary::getDword($hdr, true) - 4;
             $this->totalNum = Binary::getDword($hdr, true);
             $this->readedNum = 0;
             $this->state = static::STATE_PACKET_DATA;
         } else {
             if (($hdr = $this->lookExact(5)) === false) {
                 return;
                 // not enough data
             }
             $this->encoding = Binary::getByte($hdr);
             $pl = Binary::getDword($hdr, true);
             if ($this->getInputLength() < 5 + $pl) {
                 return;
                 // not enough data
             }
             $this->drain(5);
             $this->responseLength = $pl;
             if ($this->responseLength > $this->pool->maxAllowedPacket) {
                 $this->log('max-allowed-packet (' . $this->pool->config->maxallowedpacket->getHumanValue() . ') exceed, aborting connection');
                 $this->finish();
                 return;
             }
             if ($this->responseCode === static::REPL_ERR_NOT_FOUND) {
                 $this->drain($this->responseLength);
                 $this->result = null;
                 $this->isFinal = true;
                 $this->totalNum = 0;
                 $this->readedNum = 0;
                 $this->executeCb();
             } elseif ($this->responseCode === static::REPL_OK) {
                 $this->drain($this->responseLength);
                 $this->result = true;
                 $this->isFinal = true;
                 $this->totalNum = 0;
                 $this->readedNum = 0;
                 $this->executeCb();
             } elseif ($this->responseCode === static::REPL_ERR_MEM || $this->responseCode === static::REPL_ERR_NAN || $this->responseCode === static::REPL_ERR_LOCKED) {
                 $this->drain($this->responseLength);
                 $this->result = false;
                 $this->isFinal = true;
                 $this->totalNum = 0;
                 $this->readedNum = 0;
                 $this->executeCb();
             } else {
                 if ($this->responseCode === static::REPL_KVAL && $this->totalNum <= 0) {
                     $this->drain($this->responseLength);
                     $this->isFinal = true;
                     $this->totalNum = 0;
                     $this->readedNum = 0;
                     $this->result = [];
                     $this->executeCb();
                 } else {
                     $this->state = static::STATE_PACKET_DATA;
                 }
             }
         }
     }
     if ($this->state === static::STATE_PACKET_DATA) {
         if ($this->responseCode === static::REPL_KVAL) {
             $keyAdded = false;
             nextElement:
             $l = $this->getInputLength();
             if ($l < 9) {
                 goto cursorCall;
             }
             if (($hdr = $this->lookExact($o = 4)) === false) {
                 goto cursorCall;
             }
             $keyLen = Binary::getDword($hdr, true);
             if (($key = $this->lookExact($keyLen, $o)) === false) {
                 goto cursorCall;
             }
             $o += $keyLen;
             if (($encoding = $this->lookExact(1, $o)) === false) {
                 goto cursorCall;
             }
             $encoding = ord($encoding);
             ++$o;
             if (($hdr = $this->lookExact(4, $o)) === false) {
                 goto cursorCall;
             }
             $o += 4;
             $valLen = Binary::getDword($hdr, true);
             if ($o + $valLen > $l) {
                 goto cursorCall;
             }
             $this->drain($o);
             if ($encoding === static::GB_ENC_NUMBER) {
                 $val = $this->read($valLen);
                 $this->result[$key] = $valLen === 8 ? Binary::getQword($val, true) : Binary::getDword($val, true);
             } else {
                 $this->result[$key] = $this->read($valLen);
             }
             $keyAdded = true;
             if (++$this->readedNum >= $this->totalNum) {
                 $this->isFinal = true;
                 $this->executeCb();
                 goto start;
             } else {
                 goto nextElement;
             }
             cursorCall:
             if ($keyAdded) {
                 $this->onResponse->executeAndKeepOne($this);
             }
             return;
         } else {
             if (($this->result = $this->readExact($this->responseLength)) === false) {
                 $this->setWatermark($this->responseLength);
                 return;
             }
             $this->setWatermark(2, $this->pool->maxAllowedPacket);
             if ($this->encoding === static::GB_ENC_NUMBER) {
                 $this->result = $this->responseLength === 8 ? Binary::getQword($this->result, true) : Binary::getDword($this->result, true);
             }
             $this->isFinal = true;
             $this->totalNum = 1;
             $this->readedNum = 1;
             $this->executeCb();
         }
     }
     goto start;
 }
Esempio n. 7
0
 /**
  * Called when new data received
  * @return void
  */
 public function onRead()
 {
     packet:
     if ($this->state === self::STATE_STANDBY) {
         if ($this->bev->input->length < 4) {
             return;
         }
         $this->pctSize = Binary::bytes2int($this->read(3), true);
         $this->setWatermark($this->pctSize, $this->pctSize);
         $this->state = self::STATE_BODY;
         $this->seq = ord($this->read(1)) + 1;
     }
     /* STATE_BODY */
     $l = $this->bev->input->length;
     if ($l < $this->pctSize) {
         return;
     }
     $this->state = self::STATE_STANDBY;
     $this->setWatermark(4);
     if ($this->phase === 0) {
         $this->phase = self::PHASE_GOT_INIT;
         $this->protover = ord($this->read(1));
         if ($this->protover === 0xff) {
             // error
             $fieldCount = $this->protover;
             $this->protover = 0;
             $this->onResponse->push(function ($conn, $result) {
                 if ($conn->onConnected) {
                     $conn->connected = true;
                     $conn->onConnected->executeAll($conn, $result);
                     $conn->onConnected = null;
                 }
             });
             goto field;
         }
         if (($p = $this->search("")) === false) {
             $this->log('nul-terminator of \'serverver\' is not found');
             $this->finish();
             return;
         }
         $this->serverver = $this->read($p);
         $this->drain(1);
         // drain nul-byte
         $this->threadId = Binary::bytes2int($this->read(4), true);
         $this->scramble = $this->read(8);
         $this->drain(1);
         // ????
         $this->serverCaps = Binary::bytes2int($this->read(2), true);
         $this->serverLang = ord($this->read(1));
         $this->serverStatus = Binary::bytes2int($this->read(2), true);
         $this->drain(13);
         $restScramble = $this->read(12);
         $this->scramble .= $restScramble;
         $this->drain(1);
         $this->auth();
     } else {
         $fieldCount = ord($this->read(1));
         field:
         if ($fieldCount === 0xff) {
             // Error packet
             $u = unpack('v', $this->read(2));
             $this->errno = $u[1];
             $state = $this->read(6);
             $this->errmsg = $this->read($this->pctSize - $l + $this->bev->input->length);
             $this->onError();
             $this->errno = 0;
             $this->errmsg = '';
         } elseif ($fieldCount === 0x0) {
             // OK Packet Empty
             if ($this->phase === self::PHASE_AUTH_SENT) {
                 $this->phase = self::PHASE_HANDSHAKED;
                 if ($this->dbname !== '') {
                     $this->query('USE `' . $this->dbname . '`');
                 }
             }
             $this->affectedRows = $this->parseEncodedBinary();
             $this->insertId = $this->parseEncodedBinary();
             $u = unpack('v', $this->read(2));
             $this->serverStatus = $u[1];
             $u = unpack('v', $this->read(2));
             $this->warnCount = $u[1];
             $this->message = $this->read($this->pctSize - $l + $this->bev->input->length);
             $this->onResultDone();
         } elseif ($fieldCount === 0xfe) {
             // EOF Packet
             if ($this->rsState === self::RS_STATE_ROW) {
                 $this->onResultDone();
             } else {
                 ++$this->rsState;
             }
         } else {
             // Data packet
             $this->prependInput(chr($fieldCount));
             if ($this->rsState === self::RS_STATE_HEADER) {
                 // Result Set Header Packet
                 $extra = $this->parseEncodedBinary();
                 $this->rsState = self::RS_STATE_FIELD;
             } elseif ($this->rsState === self::RS_STATE_FIELD) {
                 // Field Packet
                 $field = ['catalog' => $this->parseEncodedString(), 'db' => $this->parseEncodedString(), 'table' => $this->parseEncodedString(), 'org_table' => $this->parseEncodedString(), 'name' => $this->parseEncodedString(), 'org_name' => $this->parseEncodedString()];
                 $this->drain(1);
                 // filler
                 $u = unpack('v', $this->read(2));
                 $field['charset'] = $u[1];
                 $u = unpack('V', $this->read(4));
                 $field['length'] = $u[1];
                 $field['type'] = ord($this->read(1));
                 $u = unpack('v', $this->read(2));
                 $field['flags'] = $u[1];
                 $field['decimals'] = ord($this->read(1));
                 $this->resultFields[] = $field;
             } elseif ($this->rsState === self::RS_STATE_ROW) {
                 // Row Packet
                 $row = [];
                 for ($i = 0, $nf = sizeof($this->resultFields); $i < $nf; ++$i) {
                     $row[$this->resultFields[$i]['name']] = $this->parseEncodedString();
                 }
                 $this->resultRows[] = $row;
             }
         }
     }
     if ($this->finished) {
         return;
     }
     $this->drain($this->pctSize - $l + $this->bev->input->length);
     // drain the rest of packet
     goto packet;
 }