diff --git a/conn/dialer.c b/conn/dialer.c index 2a5cab7..acaaacd 100644 --- a/conn/dialer.c +++ b/conn/dialer.c @@ -10,6 +10,8 @@ #include "libp2p/utils/linked_list.h" #include "multiaddr/multiaddr.h" #include "libp2p/net/multistream.h" +#include "libp2p/secio/secio.h" +#include "libp2p/yamux/yamux.h" struct TransportDialer* libp2p_conn_tcp_transport_dialer_new(); @@ -64,10 +66,10 @@ void libp2p_conn_dialer_free(struct Dialer* in) { * be used instead (which calls this method internally). * @param dialer the dialer to use * @param muiltiaddress who to connect to - * @returns a Connection, or NULL + * @returns a stream that is a ConnectionStream, or NULL */ -struct Connection* libp2p_conn_dialer_get_connection(const struct Dialer* dialer, const struct MultiAddress* multiaddress) { - struct Connection* conn = libp2p_conn_transport_dialer_get(dialer->transport_dialers, multiaddress); +struct Stream* libp2p_conn_dialer_get_connection(const struct Dialer* dialer, const struct MultiAddress* multiaddress) { + struct Stream* conn = libp2p_conn_transport_dialer_get(dialer->transport_dialers, multiaddress); if (conn == NULL) { conn = dialer->fallback_dialer->dial(dialer->fallback_dialer, multiaddress); } @@ -75,12 +77,51 @@ struct Connection* libp2p_conn_dialer_get_connection(const struct Dialer* dialer } /*** - * Attempt to connect to a particular peer + * Attempt to connect to a particular peer. This will negotiate several protocols * @param dialer the dialer * @param peer the peer to join + * @returns true(1) on success, false(0) otherwise */ int libp2p_conn_dialer_join_swarm(const struct Dialer* dialer, struct Libp2pPeer* peer, int timeout_secs) { - return 0; + // find the right Multiaddress + struct Libp2pLinkedList* current_entry = peer->addr_head; + struct Stream* conn_stream = NULL; + while (current_entry != NULL) { + struct MultiAddress* ma = current_entry->item; + conn_stream = libp2p_conn_dialer_get_connection(dialer, ma); + if (conn_stream != NULL) { + break; + } + current_entry = current_entry->next; + } + if (conn_stream == NULL) + return 0; + peer->sessionContext->insecure_stream = conn_stream; + peer->sessionContext->default_stream = conn_stream; + // multistream + struct Stream* new_stream = libp2p_net_multistream_stream_new(conn_stream); + if (new_stream != NULL) { + // secio over multistream + new_stream = libp2p_secio_stream_new(new_stream); + if (new_stream != NULL) { + peer->sessionContext->default_stream = new_stream; + // multistream over secio + new_stream = libp2p_net_multistream_stream_new(new_stream); + if (new_stream != NULL) { + peer->sessionContext->default_stream = new_stream; + // yamux over multistream + new_stream = libp2p_yamux_stream_new(new_stream); + if (new_stream != NULL) { + peer->sessionContext->default_stream = new_stream; + // identity over yamux + // kademlia over yamux + // circuit relay over yamux + } + } + } + } + + return 1; } /** @@ -90,17 +131,7 @@ int libp2p_conn_dialer_join_swarm(const struct Dialer* dialer, struct Libp2pPeer * @param protocol the protocol to use (right now only 'multistream' is supported) * @returns the ready-to-use stream */ -struct Stream* libp2p_conn_dialer_get_stream(const struct Dialer* dialer, const struct MultiAddress* multiaddress, const char* protocol) { - // this is a shortcut for now. Other protocols will soon be implemented - if (strcmp(protocol, "multistream") != 0) - return NULL; - char* ip; - int port = multiaddress_get_ip_port(multiaddress); - if (!multiaddress_get_ip_address(multiaddress, &ip)) { - free(ip); - return NULL; - } - struct Stream* stream = libp2p_net_multistream_connect(ip, port); - free(ip); - return stream; +struct Stream* libp2p_conn_dialer_get_stream(const struct Dialer* dialer, const struct Libp2pPeer* peer, const char* protocol) { + // TODO: Implement this method + return NULL; } diff --git a/conn/tcp_transport_dialer.c b/conn/tcp_transport_dialer.c index 3f4b223..3d8ab32 100644 --- a/conn/tcp_transport_dialer.c +++ b/conn/tcp_transport_dialer.c @@ -4,6 +4,7 @@ #include "multiaddr/multiaddr.h" #include "libp2p/net/p2pnet.h" +#include "libp2p/net/connectionstream.h" #include "libp2p/conn/connection.h" #include "libp2p/conn/transport_dialer.h" #include "multiaddr/multiaddr.h" @@ -13,36 +14,20 @@ */ int libp2p_conn_tcp_can_handle(const struct MultiAddress* addr) { - return 1; + return multiaddress_is_ip(addr); } -int libp2p_conn_tcp_read(const struct Connection* connection, char** out, size_t* num_bytes) { - int buffer_size = 65535; - *out = (char*)malloc(buffer_size); - ssize_t bytes = socket_read(connection->socket_handle, *out, buffer_size, 0, 5); - *num_bytes = bytes; - return bytes > 0; -} - -int libp2p_conn_tcp_write(const struct Connection* connection, const char* in, size_t num_bytes) { - ssize_t bytes = socket_write(connection->socket_handle, in, num_bytes, 0); - return bytes == num_bytes; -} - -struct Connection* libp2p_conn_tcp_dial(const struct TransportDialer* transport_dialer, const struct MultiAddress* addr) { - struct Connection* conn = (struct Connection*) malloc(sizeof(struct Connection)); - conn->socket_handle = socket_open4(); +struct Stream* libp2p_conn_tcp_dial(const struct TransportDialer* transport_dialer, const struct MultiAddress* addr) { + int socket_descriptor = socket_open4(); char* ip; int port = multiaddress_get_ip_port(addr); if (!multiaddress_get_ip_address(addr, &ip)) return NULL; - struct hostent* host = gethostbyname(ip); + + struct Stream* stream = libp2p_net_connection_new(socket_descriptor, ip, port); free(ip); - struct in_addr** addr_list = (struct in_addr**)host->h_addr_list; - socket_connect4(conn->socket_handle, (*addr_list[0]).s_addr, port); - conn->read = libp2p_conn_tcp_read; - conn->write = libp2p_conn_tcp_write; - return conn; + + return stream; } struct TransportDialer* libp2p_conn_tcp_transport_dialer_new(char* peer_id, struct PrivateKey* private_key) { diff --git a/conn/transport_dialer.c b/conn/transport_dialer.c index 88fb044..21f6e5e 100644 --- a/conn/transport_dialer.c +++ b/conn/transport_dialer.c @@ -33,7 +33,7 @@ void libp2p_conn_transport_dialer_free(struct TransportDialer* in) { * @param multiaddr the address * @returns a connection, or NULL if no appropriate dialer was found */ -struct Connection* libp2p_conn_transport_dialer_get(const struct Libp2pLinkedList* transport_dialers, const struct MultiAddress* multiaddr) { +struct Stream* libp2p_conn_transport_dialer_get(const struct Libp2pLinkedList* transport_dialers, const struct MultiAddress* multiaddr) { const struct Libp2pLinkedList* current = transport_dialers; struct TransportDialer* t_dialer = NULL; while (current != NULL) { diff --git a/include/libp2p/conn/dialer.h b/include/libp2p/conn/dialer.h index da6a5c8..bbe9f78 100644 --- a/include/libp2p/conn/dialer.h +++ b/include/libp2p/conn/dialer.h @@ -65,14 +65,14 @@ int libp2p_conn_dialer_join_swarm(const struct Dialer* dialer, struct Libp2pPeer * @param muiltiaddress who to connect to * @returns a Connection, or NULL */ -struct Connection* libp2p_conn_dialer_get_connection(const struct Dialer* dialer, const struct MultiAddress* multiaddress); +struct Stream* libp2p_conn_dialer_get_connection(const struct Dialer* dialer, const struct MultiAddress* multiaddress); /** * return a Stream that is already set up to use the passed in protocol * @param dialer the dialer to use - * @param multiaddress the host to dial + * @param peer the host * @param protocol the protocol to use (right now only 'multistream' is supported) * @returns the ready-to-use stream */ -struct Stream* libp2p_conn_dialer_get_stream(const struct Dialer* dialer, const struct MultiAddress* multiaddress, const char* protocol); +struct Stream* libp2p_conn_dialer_get_stream(const struct Dialer* dialer, const struct Libp2pPeer* peer, const char* protocol); diff --git a/include/libp2p/conn/transport_dialer.h b/include/libp2p/conn/transport_dialer.h index d767005..5ac8977 100644 --- a/include/libp2p/conn/transport_dialer.h +++ b/include/libp2p/conn/transport_dialer.h @@ -1,16 +1,17 @@ #pragma once #include "multiaddr/multiaddr.h" +#include "libp2p/net/stream.h" #include "libp2p/utils/linked_list.h" struct TransportDialer { char* peer_id; struct PrivateKey* private_key; int (*can_handle)(const struct MultiAddress* multiaddr); - struct Connection* (*dial)(const struct TransportDialer* transport_dialer, const struct MultiAddress* multiaddr); + struct Stream* (*dial)(const struct TransportDialer* transport_dialer, const struct MultiAddress* multiaddr); }; struct TransportDialer* libp2p_conn_transport_dialer_new(char* peer_id, struct PrivateKey* private_key); void libp2p_conn_transport_dialer_free(struct TransportDialer* in); -struct Connection* libp2p_conn_transport_dialer_get(const struct Libp2pLinkedList* transport_dialers, const struct MultiAddress* multiaddr); +struct Stream* libp2p_conn_transport_dialer_get(const struct Libp2pLinkedList* transport_dialers, const struct MultiAddress* multiaddr); diff --git a/include/libp2p/net/multistream.h b/include/libp2p/net/multistream.h index 577911b..ed17b73 100644 --- a/include/libp2p/net/multistream.h +++ b/include/libp2p/net/multistream.h @@ -95,6 +95,6 @@ int libp2p_net_multistream_negotiate(struct SessionContext* session); */ struct KademliaMessage* libp2p_net_multistream_get_message(struct Stream* stream); -struct Stream* libp2p_net_multistream_stream_new(int socket_fd, const char* ip, int port); +struct Stream* libp2p_net_multistream_stream_new(struct Stream* parent_stream); void libp2p_net_multistream_stream_free(struct Stream* stream); diff --git a/include/libp2p/net/stream.h b/include/libp2p/net/stream.h index 447b4e9..d818af1 100644 --- a/include/libp2p/net/stream.h +++ b/include/libp2p/net/stream.h @@ -1,6 +1,7 @@ #pragma once #include +#include /** * Encapsulates a message that (was/will be) sent @@ -94,6 +95,10 @@ struct Stream { int (*peek)(void* stream_context); }; +struct Stream* libp2p_stream_new(); + +void libp2p_stream_free(struct Stream* stream); + /*** * Attempt to lock a stream for personal use. Does not block. * @param stream the stream to lock diff --git a/include/libp2p/secio/secio.h b/include/libp2p/secio/secio.h index 0a8c865..7fe07bd 100644 --- a/include/libp2p/secio/secio.h +++ b/include/libp2p/secio/secio.h @@ -20,12 +20,12 @@ struct SecioContext { struct Libp2pProtocolHandler* libp2p_secio_build_protocol_handler(struct RsaPrivateKey* private_key, struct Peerstore* peer_store); /*** - * performs initial communication over an insecure channel to share - * keys, IDs, and initiate connection. This is a framed messaging system - * @param ctx the SecioContext - * @returns true(1) on success, false(0) otherwise + * Initiates a secio handshake. Use this method when you want to initiate a secio + * session. This should not be used to respond to incoming secio requests + * @param parent_stream the parent stream + * @returns a Secio Stream */ -int libp2p_secio_handshake(struct SecioContext* ctx); +struct Stream* libp2p_secio_stream_new(struct Stream* parent_stream); /*** * Initiates a secio handshake. Use this method when you want to initiate a secio @@ -47,3 +47,15 @@ int libp2p_secio_send_protocol(struct SecioContext* session); * @returns true(1) if we received what we think we should have, false(0) otherwise */ int libp2p_secio_receive_protocol(struct SecioContext* session); + +/*** + * performs initial communication over an insecure channel to share + * keys, IDs, and initiate connection. This is a framed messaging system + * NOTE: session must contain a valid socket_descriptor that is a multistream. + * @param local_session the secure session to be filled + * @param private_key our private key to use + * @param peerstore the collection of peers + * @returns true(1) on success, false(0) otherwise + */ +int libp2p_secio_handshake(struct SecioContext* secio_context); + diff --git a/include/libp2p/yamux/yamux.h b/include/libp2p/yamux/yamux.h index e45d365..5f1fa85 100644 --- a/include/libp2p/yamux/yamux.h +++ b/include/libp2p/yamux/yamux.h @@ -1,6 +1,11 @@ #pragma once #include "libp2p/net/protocol.h" +#include "libp2p/net/stream.h" + +/*** + * Declarations for the Yamux protocol + */ /** * Build a handler that can handle the yamux protocol @@ -21,3 +26,5 @@ int yamux_send_protocol(struct SessionContext* context); * @returns true(1) on success, false(0) otherwise */ int yamux_receive_protocol(struct SessionContext* context); + +struct Stream* libp2p_yamux_stream_new(struct Stream* parent_stream); diff --git a/net/Makefile b/net/Makefile index 715d342..74eb944 100644 --- a/net/Makefile +++ b/net/Makefile @@ -7,7 +7,7 @@ endif LFLAGS = DEPS = -OBJS = sctp.o socket.o tcp.o udp.o multistream.o protocol.o connectionstream.o +OBJS = sctp.o socket.o tcp.o udp.o multistream.o protocol.o connectionstream.o stream.o %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) diff --git a/net/multistream.c b/net/multistream.c index df11f89..fe793fa 100644 --- a/net/multistream.c +++ b/net/multistream.c @@ -310,7 +310,8 @@ struct Stream* libp2p_net_multistream_connect_with_timeout(const char* hostname, goto exit; // send the multistream handshake - stream = libp2p_net_multistream_stream_new(socket, hostname, port); + // TODO: wire this back in + //stream = libp2p_net_multistream_stream_new(socket, hostname, port, NULL); if (stream == NULL) goto exit; @@ -391,17 +392,15 @@ void libp2p_net_multistream_stream_free(struct Stream* stream) { * @param ip the IP address * @param port the port */ -struct Stream* libp2p_net_multistream_stream_new(int socket_fd, const char* ip, int port) { +struct Stream* libp2p_net_multistream_stream_new(struct Stream* parent_stream) { struct Stream* out = (struct Stream*)malloc(sizeof(struct Stream)); if (out != NULL) { - out->parent_stream = NULL; + out->parent_stream = parent_stream; out->close = libp2p_net_multistream_close; out->read = libp2p_net_multistream_read; out->write = libp2p_net_multistream_write; out->peek = libp2p_net_multistream_peek; - char str[strlen(ip) + 50]; - sprintf(str, "/ip4/%s/tcp/%d", ip, port); - out->address = multiaddress_new_from_string(str); + out->address = parent_stream->address; } return out; } diff --git a/net/stream.c b/net/stream.c new file mode 100644 index 0000000..42bd9d1 --- /dev/null +++ b/net/stream.c @@ -0,0 +1,25 @@ +#include + +#include "libp2p/net/stream.h" + +struct Stream* libp2p_stream_new() { + struct Stream* stream = (struct Stream*) malloc(sizeof(struct Stream)); + if (stream != NULL) { + stream->address = NULL; + stream->close = NULL; + stream->parent_stream = NULL; + stream->peek = NULL; + stream->read = NULL; + stream->read_raw = NULL; + stream->socket_mutex = NULL; + stream->stream_context = NULL; + stream->write = NULL; + } + return stream; +} + +void libp2p_stream_free(struct Stream* stream) { + if (stream != NULL) { + free(stream); + } +} diff --git a/secio/secio.c b/secio/secio.c index 5ab66e8..58dc5f4 100644 --- a/secio/secio.c +++ b/secio/secio.c @@ -72,17 +72,28 @@ int libp2p_secio_shutdown(void* context) { /*** * Initiates a secio handshake. Use this method when you want to initiate a secio * session. This should not be used to respond to incoming secio requests - * @param session_context the session context - * @param private_key the RSA private key to use - * @param peer_store the peer store - * @returns true(1) on success, false(0) otherwise + * @param parent_stream the parent stream + * @returns a Secio Stream */ -int libp2p_secio_initiate_handshake(struct SecioContext* ctx) { - if (libp2p_secio_send_protocol(ctx) && libp2p_secio_receive_protocol(ctx)) { - return libp2p_secio_handshake(ctx); +struct Stream* libp2p_secio_stream_new(struct Stream* parent_stream) { + struct Stream* new_stream = libp2p_stream_new(); + if (new_stream != NULL) { + struct SecioContext* ctx = (struct SecioContext*) malloc(sizeof(struct SecioContext)); + if (ctx == NULL) { + libp2p_stream_free(new_stream); + new_stream = NULL; + return NULL; + } + new_stream->stream_context = ctx; + new_stream->parent_stream = parent_stream; + if (!libp2p_secio_send_protocol(ctx) + || !libp2p_secio_receive_protocol(ctx) + || !libp2p_secio_handshake(ctx)) { + libp2p_stream_free(new_stream); + new_stream = NULL; + } } - libp2p_logger_error("secio", "Secio protocol exchange failed.\n"); - return 0; + return new_stream; } struct Libp2pProtocolHandler* libp2p_secio_build_protocol_handler(struct RsaPrivateKey* private_key, struct Peerstore* peer_store) { diff --git a/test/test_conn.h b/test/test_conn.h index b68267f..a8a5b32 100644 --- a/test/test_conn.h +++ b/test/test_conn.h @@ -29,7 +29,7 @@ int test_dialer_dial() { struct PrivateKey* private_key = NULL; struct Dialer* dialer = NULL; struct MultiAddress* destination_address = NULL; - struct Connection* conn = NULL; + struct Stream* conn = NULL; char* result = NULL; size_t result_size = 0; struct Libp2pPeer* peer = libp2p_peer_new(); @@ -60,7 +60,6 @@ int test_dialer_dial() { free(peer_id); multiaddress_free(destination_address); libp2p_conn_dialer_free(dialer); - libp2p_conn_connection_free(conn); return retVal; } @@ -92,7 +91,7 @@ int test_dialer_dial_multistream() { goto exit; // now try to dial - stream = libp2p_conn_dialer_get_stream(dialer, destination_address, "multistream"); + stream = libp2p_conn_dialer_get_stream(dialer, peer, "multistream"); if (stream == NULL) goto exit; diff --git a/test/test_secio.h b/test/test_secio.h index 5588ef1..0560913 100644 --- a/test/test_secio.h +++ b/test/test_secio.h @@ -95,33 +95,6 @@ int test_secio_handshake() { } // attempt to write the protocol, and see what comes back - /* - char* protocol = "/secio/1.0.0\n"; - int protocol_size = strlen(protocol); - secure_session->insecure_stream->write(secure_session, (unsigned char*)protocol, protocol_size); - - unsigned char* buffer = NULL; - size_t bytes_read = 0; - int timeout = 30; - secure_session->insecure_stream->read(secure_session, &buffer, &bytes_read, timeout); - - if (!libp2p_secio_handshake(secure_session, rsa_private_key, peerstore)) { - fprintf(stderr, "test_secio_handshake: Unable to do handshake\n"); - if (secure_session->shared_key != NULL) { - fprintf(stdout, "Shared key: "); - for(int i = 0; i < secure_session->shared_key_size; i++) - fprintf(stdout, "%d ", secure_session->shared_key[i]); - fprintf(stdout, "\nLocal stretched key: "); - print_stretched_key(secure_session->local_stretched_key); - fprintf(stdout, "\nRemote stretched key: "); - print_stretched_key(secure_session->remote_stretched_key); - fprintf(stdout, "\n"); - } - goto exit; - } - */ - /* - // a new way to do the above if (!libp2p_secio_initiate_handshake(secure_session, rsa_private_key, peerstore)) { libp2p_logger_error("test_secio", "Unable to do handshake\n"); if (secure_session->shared_key != NULL) { @@ -137,17 +110,6 @@ int test_secio_handshake() { goto exit; } - /* - fprintf(stdout, "Shared key: "); - for(int i = 0; i < secure_session.shared_key_size; i++) - fprintf(stdout, "%d ", secure_session.shared_key[i]); - fprintf(stdout, "\nLocal stretched key: "); - print_stretched_key(secure_session.local_stretched_key); - fprintf(stdout, "\nRemote stretched key: "); - print_stretched_key(secure_session.remote_stretched_key); - fprintf(stdout, "\n"); - */ - /* // now attempt to do something with it... try to negotiate a multistream if (libp2p_net_multistream_negotiate(secure_session) == 0) { fprintf(stdout, "Unable to negotiate multistream\n"); @@ -197,27 +159,6 @@ int test_secio_handshake() { exit: if (peerstore != NULL) libp2p_peerstore_free(peerstore); - /* - if (secure_session.insecure_stream != NULL) - libp2p_net_multistream_stream_free(secure_session.insecure_stream); - if (secure_session.local_stretched_key != NULL) - libp2p_crypto_ephemeral_stretched_key_free(secure_session.local_stretched_key); - if (secure_session.remote_stretched_key != NULL) - libp2p_crypto_ephemeral_stretched_key_free(secure_session.remote_stretched_key); - if (secure_session.ephemeral_private_key != NULL) - libp2p_crypto_ephemeral_key_free(secure_session.ephemeral_private_key); - if (secure_session.remote_ephemeral_public_key != NULL) - free(secure_session.remote_ephemeral_public_key); - if (secure_session.chosen_cipher != NULL) - free(secure_session.chosen_cipher); - if (secure_session.chosen_curve != NULL) - free(secure_session.chosen_curve); - if (secure_session.chosen_hash != NULL) - free(secure_session.chosen_hash); - if (secure_session.shared_key != NULL) - free(secure_session.shared_key); - */ - /* if (private_key != NULL) libp2p_crypto_private_key_free(private_key); if (decode_base64 != NULL) @@ -442,32 +383,6 @@ int test_secio_handshake_go() { } // attempt to write the protocol, and see what comes back - /* - char* protocol = "/secio/1.0.0\n"; - int protocol_size = strlen(protocol); - secure_session->insecure_stream->write(secure_session, (unsigned char*)protocol, protocol_size); - - unsigned char* buffer = NULL; - size_t bytes_read = 0; - int timeout = 30; - secure_session->insecure_stream->read(secure_session, &buffer, &bytes_read, timeout); - if (!libp2p_secio_handshake(secure_session, rsa_private_key, peerstore)) { - fprintf(stderr, "test_secio_handshake: Unable to do handshake\n"); - if (secure_session->shared_key != NULL) { - fprintf(stdout, "Shared key: "); - for(int i = 0; i < secure_session->shared_key_size; i++) - fprintf(stdout, "%d ", secure_session->shared_key[i]); - fprintf(stdout, "\nLocal stretched key: "); - print_stretched_key(secure_session->local_stretched_key); - fprintf(stdout, "\nRemote stretched key: "); - print_stretched_key(secure_session->remote_stretched_key); - fprintf(stdout, "\n"); - } - goto exit; - } - */ - /* - // a new way to do the above if (!libp2p_secio_initiate_handshake(secure_session, rsa_private_key, peerstore)) { libp2p_logger_error("test_secio", "Unable to do handshake.\n"); if (secure_session->shared_key != NULL) { @@ -483,17 +398,6 @@ int test_secio_handshake_go() { goto exit; } - /* - fprintf(stdout, "Shared key: "); - for(int i = 0; i < secure_session.shared_key_size; i++) - fprintf(stdout, "%d ", secure_session.shared_key[i]); - fprintf(stdout, "\nLocal stretched key: "); - print_stretched_key(secure_session.local_stretched_key); - fprintf(stdout, "\nRemote stretched key: "); - print_stretched_key(secure_session.remote_stretched_key); - fprintf(stdout, "\n"); - */ - /* // now attempt to do something with it... try to negotiate a multistream if (libp2p_net_multistream_negotiate(secure_session) == 0) { fprintf(stdout, "Unable to negotiate multistream\n"); diff --git a/yamux/yamux.c b/yamux/yamux.c index 557f743..3d90bc1 100644 --- a/yamux/yamux.c +++ b/yamux/yamux.c @@ -170,3 +170,12 @@ struct Libp2pProtocolHandler* yamux_build_protocol_handler(struct Libp2pVector* } return handler; } + +/*** + * Negotiate the Yamux protocol + * @param parent_stream the parent stream + * @returns a Stream initialized and ready for yamux + */ +struct Stream* libp2p_yamux_stream_new(struct Stream* parent_stream) { + return NULL; +}