Exemplo n.º 1
0
bool NATTraversalProtocol::SignalInputData(IOBuffer &buffer, sockaddr_in *pPeerAddress) {
	//FINEST("_inputBuffer:\n%s", STR(buffer));
	buffer.IgnoreAll();
	if (_pOutboundAddress == NULL)
		return true;
	if (_pOutboundAddress->sin_addr.s_addr != pPeerAddress->sin_addr.s_addr) {
		WARN("Attempt to divert traffic. DoS attack!?");
		return true;
	}
	string ipAddress = inet_ntoa(_pOutboundAddress->sin_addr);
	if (_pOutboundAddress->sin_port == pPeerAddress->sin_port) {
		INFO("The client has public endpoint: %s:%"PRIu16,
				STR(ipAddress),
				ENTOHS(_pOutboundAddress->sin_port));
	} else {
		INFO("The client is behind firewall: %s:%"PRIu16" -> %s:%"PRIu16,
				STR(ipAddress),
				ENTOHS(_pOutboundAddress->sin_port),
				STR(ipAddress),
				ENTOHS(pPeerAddress->sin_port));
		_pOutboundAddress->sin_port = pPeerAddress->sin_port;
	}
	_pOutboundAddress = NULL;
	return true;
}
Exemplo n.º 2
0
bool TCPAcceptor::Drop() {
    sockaddr address;
    memset(&address, 0, sizeof (sockaddr));
    socklen_t len = sizeof (sockaddr);


    //1. Accept the connection
    int32_t fd = accept(_inboundFd, &address, &len);
    if (fd < 0) {
        uint32_t err = LASTSOCKETERROR;
        WARN("Accept failed. Error code was: %"PRIu32, err);
        return true;
    }

    //2. Drop it now
    CLOSE_SOCKET(fd);
    _droppedCount++;

    INFO("Client explicitly dropped: %s:%"PRIu16" -> %s:%"PRIu16,
         inet_ntoa(((sockaddr_in *) & address)->sin_addr),
         ENTOHS(((sockaddr_in *) & address)->sin_port),
         STR(_ipAddress),
         _port);
    return true;
}
Exemplo n.º 3
0
bool TCPAcceptor::StartAccept(BaseClientApplication *pApplication) {
	_pApplication = pApplication;

	_inboundFd = _outboundFd = (int) socket(PF_INET, SOCK_STREAM, 0);
	if (_inboundFd < 0) {
		int err = LASTSOCKETERROR;
		FATAL("Unable to create socket: %s(%d)", strerror(err), err);
		return false;
	}

	if (!setFdOptions(_inboundFd)) {
		FATAL("Unable to set socket options");
		return false;
	}

	if (bind(_inboundFd, (sockaddr *) & _address, sizeof (sockaddr)) != 0) {
		int error = LASTSOCKETERROR;
		FATAL("Unable to bind on address: tcp://%s:%hu; Error was: %s (%d)",
				inet_ntoa(((sockaddr_in *) & _address)->sin_addr),
				ENTOHS(((sockaddr_in *) & _address)->sin_port),
				strerror(error),
				error);
		return false;
	}

	if (listen(_inboundFd, 100) != 0) {
		FATAL("Unable to put the socket in listening mode");
		return false;
	}

	_enabled = true;

	return IOHandlerManager::EnableAcceptConnections(this);
}
Exemplo n.º 4
0
bool TCPAcceptor::Drop() {
	sockaddr address;
	memset(&address, 0, sizeof (sockaddr));
	socklen_t len = sizeof (sockaddr);


	//1. Accept the connection
	int32_t fd = accept(_inboundFd, &address, &len);
	if ((fd < 0) || (!setFdCloseOnExec(fd))) {
		int err = errno;
		if (err != EWOULDBLOCK)
			WARN("Accept failed. Error code was: (%d) %s", err, strerror(err));
		return false;
	}

	//2. Drop it now
	CLOSE_SOCKET(fd);
	_droppedCount++;

	INFO("Client explicitly dropped: %s:%"PRIu16" -> %s:%"PRIu16,
			inet_ntoa(((sockaddr_in *) & address)->sin_addr),
			ENTOHS(((sockaddr_in *) & address)->sin_port),
			STR(_ipAddress),
			_port);
	return true;
}
Exemplo n.º 5
0
bool MmapFile::PeekI16(int16_t *pValue, bool networkOrder) {
	if (!PeekBuffer((uint8_t *) pValue, 2))
		return false;
	if (networkOrder)
		*pValue = ENTOHS(*pValue); //----MARKED-SHORT----
	return true;
}
Exemplo n.º 6
0
void Rtcp::send_sdes()
{
    if (!m_subsess) return;

    memset(m_peer_sdes_buf, 0, sizeof(m_peer_sdes_buf));

    RtcpCommon *common = (RtcpCommon *) m_peer_sdes_buf;
    common->version = 2;    // Version: RFC 1889 Version (2)
    common->p = 0;          // Padding
    common->count = 1;      // Source count
    common->pt = RTCP_SDES; // Source description (202)
    common->ssrc = strtol(m_subsess->session_id(), NULL, 16);

    uint8_t *p = (uint8_t *) (m_peer_sdes_buf + sizeof(RtcpCommon)),
            *psave = p;
    const char *cname = m_subsess->parent_session().CNAME();
    int len = strlen(cname);
    put_byte(p, RTCP_SDES_CNAME);
    put_byte(p, len);
    strncpy((char *) p, cname, len); p += len;
    put_byte(p, RTCP_SDES_NULL);

    common->length = EHTONS((p-psave+4+4+3)/4-1); // Length

    if (m_interface->write((uint8_t *) m_peer_sdes_buf, (ENTOHS(common->length)+1)*4) < 0) {
        LOGE("Send RTCP SDES to server failed");
        // Fall through
    }
}
Exemplo n.º 7
0
bool TCPCarrier::GetEndpointsInfo() {
	socklen_t len = sizeof (sockaddr);
	if (getpeername(_inboundFd, (sockaddr *) & _farAddress, &len) != 0) {
		FATAL("Unable to get peer's address");
		return false;
	}
	_farIp = format("%s", inet_ntoa(((sockaddr_in *) & _farAddress)->sin_addr));
	_farPort = ENTOHS(((sockaddr_in *) & _farAddress)->sin_port); //----MARKED-SHORT----

	if (getsockname(_inboundFd, (sockaddr *) & _nearAddress, &len) != 0) {
		FATAL("Unable to get peer's address");
		return false;
	}
	_nearIp = format("%s", inet_ntoa(((sockaddr_in *) & _nearAddress)->sin_addr));
	_nearPort = ENTOHS(((sockaddr_in *) & _nearAddress)->sin_port); //----MARKED-SHORT----
	return true;
}
Exemplo n.º 8
0
bool TCPAcceptor::Bind() {
	_inboundFd = _outboundFd = (int) socket(PF_INET, SOCK_STREAM, 0); //NOINHERIT
	if (_inboundFd < 0) {
		int err = errno;
		FATAL("Unable to create socket: (%d) %s", err, strerror(err));
		return false;
	}

	if (!setFdOptions(_inboundFd, false)) {
		FATAL("Unable to set socket options");
		return false;
	}

	if (bind(_inboundFd, (sockaddr *) & _address, sizeof (sockaddr)) != 0) {
		int err = errno;
		FATAL("Unable to bind on address: tcp://%s:%hu; Error was: (%d) %s",
				inet_ntoa(((sockaddr_in *) & _address)->sin_addr),
				ENTOHS(((sockaddr_in *) & _address)->sin_port),
				err,
				strerror(err));
		return false;
	}

	if (_port == 0) {
		socklen_t tempSize = sizeof (sockaddr);
		if (getsockname(_inboundFd, (sockaddr *) & _address, &tempSize) != 0) {
			FATAL("Unable to extract the random port");
			return false;
		}
		_parameters[CONF_PORT] = (uint16_t) ENTOHS(_address.sin_port);
	}

	if (listen(_inboundFd, 100) != 0) {
		FATAL("Unable to put the socket in listening mode");
		return false;
	}

	_enabled = true;
	return true;
}
Exemplo n.º 9
0
void Rtcp::network_read_handler1(int mask)
{
    uint8_t buf[MaxRTCPPacketSize];
    int nread;

    if ((nread = m_interface->read(buf, sizeof(buf))) < 0) {
        LOGE("Read RTCP packet failed");
        return;
    }

    uint8_t *p = buf, *pend = p + nread;
    while (p < pend) {
        RtcpCommon *common = (RtcpCommon *) p;
        unsigned len = (ENTOHS((uint16_t) common->length) + 1)*4;

        switch (common->pt) {
        case RTCP_SR:
        case RTCP_RR:
        case RTCP_XR:
            parse_rtcp_SR_RR(p, len);
            break;

        case RTCP_SDES:
            parse_rtcp_SDES(p, len);
            break;

        case RTCP_BYE:
            parse_rtcp_BYE(p, len);
            break;

        default:
            break;
        }

        p += len;
    }
}
Exemplo n.º 10
0
bool TCPAcceptor::Accept() {
    sockaddr address;
    memset(&address, 0, sizeof (sockaddr));
    socklen_t len = sizeof (sockaddr);
    int32_t fd;
    int32_t error;

    //1. Accept the connection
    fd = accept(_inboundFd, &address, &len);
    error = errno;
    if (fd < 0) {
        FATAL("Unable to accept client connection: %s (%d)", strerror(error), error);
        return false;
    }
    if (!_enabled) {
        CLOSE_SOCKET(fd);
        _droppedCount++;
        WARN("Acceptor is not enabled. Client dropped: %s:%"PRIu16" -> %s:%"PRIu16,
             inet_ntoa(((sockaddr_in *) & address)->sin_addr),
             ENTOHS(((sockaddr_in *) & address)->sin_port),
             STR(_ipAddress),
             _port);
        return true;
    }
    INFO("Client connected: %s:%"PRIu16" -> %s:%"PRIu16,
         inet_ntoa(((sockaddr_in *) & address)->sin_addr),
         ENTOHS(((sockaddr_in *) & address)->sin_port),
         STR(_ipAddress),
         _port);

    if (!setFdOptions(fd)) {
        FATAL("Unable to set socket options");
        CLOSE_SOCKET(fd);
        return false;
    }

    //4. Create the chain
    BaseProtocol *pProtocol = ProtocolFactoryManager::CreateProtocolChain(_protocolChain, _parameters);
    if (pProtocol == NULL) {
        FATAL("Unable to create protocol chain");
        CLOSE_SOCKET(fd);
        return false;
    }

    //5. Create the carrier and bind it
    TCPCarrier *pTCPCarrier = new TCPCarrier(fd);
    pTCPCarrier->SetProtocol(pProtocol->GetFarEndpoint());
    pProtocol->GetFarEndpoint()->SetIOHandler(pTCPCarrier);

    //6. Register the protocol stack with an application
    if (_pApplication != NULL) {
        pProtocol = pProtocol->GetNearEndpoint();
        pProtocol->SetApplication(_pApplication);
    }

    if (pProtocol->GetNearEndpoint()->GetOutputBuffer() != NULL)
        pProtocol->GetNearEndpoint()->EnqueueForOutbound();

    _acceptedCount++;

    //7. Done
    return true;
}
Exemplo n.º 11
0
void CommonTestsSuite::test_Endianess() {
	uint16_t ui16 = 0x0102;
	uint32_t ui32 = 0x01020304;
	uint64_t ui64 = 0x0102030405060708LL;
	double d = 123.456;

	//host to network
	uint8_t *pBuffer = NULL;
	ui16 = EHTONS(ui16);
	pBuffer = (uint8_t *) & ui16;
	TS_ASSERT(pBuffer[0] == 0x01);
	TS_ASSERT(pBuffer[1] == 0x02);

	pBuffer = NULL;
	ui32 = EHTONL(ui32);
	pBuffer = (uint8_t *) & ui32;
	TS_ASSERT(pBuffer[0] == 0x01);
	TS_ASSERT(pBuffer[1] == 0x02);
	TS_ASSERT(pBuffer[2] == 0x03);
	TS_ASSERT(pBuffer[3] == 0x04);

	pBuffer = NULL;
	ui32 = 0x01020304;
	ui32 = EHTONA(ui32);
	pBuffer = (uint8_t *) & ui32;
	TS_ASSERT(pBuffer[0] == 0x02);
	TS_ASSERT(pBuffer[1] == 0x03);
	TS_ASSERT(pBuffer[2] == 0x04);
	TS_ASSERT(pBuffer[3] == 0x01);

	pBuffer = NULL;
	ui64 = EHTONLL(ui64);
	pBuffer = (uint8_t *) & ui64;
	TS_ASSERT(pBuffer[0] == 0x01);
	TS_ASSERT(pBuffer[1] == 0x02);
	TS_ASSERT(pBuffer[2] == 0x03);
	TS_ASSERT(pBuffer[3] == 0x04);
	TS_ASSERT(pBuffer[4] == 0x05);
	TS_ASSERT(pBuffer[5] == 0x06);
	TS_ASSERT(pBuffer[6] == 0x07);
	TS_ASSERT(pBuffer[7] == 0x08);

	pBuffer = NULL;
	EHTOND(d, ui64);
	pBuffer = (uint8_t *) & ui64;
	TS_ASSERT(pBuffer[0] == 0x40);
	TS_ASSERT(pBuffer[1] == 0x5e);
	TS_ASSERT(pBuffer[2] == 0xdd);
	TS_ASSERT(pBuffer[3] == 0x2f);
	TS_ASSERT(pBuffer[4] == 0x1a);
	TS_ASSERT(pBuffer[5] == 0x9f);
	TS_ASSERT(pBuffer[6] == 0xbe);
	TS_ASSERT(pBuffer[7] == 0x77);

	//network to host pointer
	char buffer[] = {
		0x00, 0x01, 0x02, 0x03,
		0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0a, 0x0b,
		0x0c, 0x0d, 0x0e, 0x0f,
		0x00, 0x01, 0x02, 0x03,
		0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0a, 0x0b,
		0x0c, 0x0d, 0x0e, 0x0f,
		0x00, 0x01, 0x02, 0x03,
		0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0a, 0x0b,
		0x0c, 0x0d, 0x0e, 0x0f,
		0x00, 0x01, 0x02, 0x03,
		0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0a, 0x0b,
		0x0c, 0x0d, 0x0e, 0x0f,
		0x00, 0x01, 0x02, 0x03,
		0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0a, 0x0b,
		0x0c, 0x0d, 0x0e, 0x0f,
		0x00, 0x01, 0x02, 0x03,
		0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0a, 0x0b,
		0x0c, 0x0d, 0x0e, 0x0f,
		0x00, 0x01, 0x02, 0x03,
		0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0a, 0x0b,
		0x0c, 0x0d, 0x0e, 0x0f,
		0x00, 0x01, 0x02, 0x03,
		0x04, 0x05, 0x06, 0x07,
		0x08, 0x09, 0x0a, 0x0b,
		0x0c, 0x0d, 0x0e, 0x0f
	};

	ui16 = ENTOHSP(buffer);
	TS_ASSERT(ui16 == 0x0001);
	ui16 = ENTOHSP(buffer + 1);
	TS_ASSERT(ui16 == 0x0102);
	ui16 = ENTOHSP(buffer + 2);
	TS_ASSERT(ui16 == 0x0203);
	ui16 = ENTOHSP(buffer + 3);
	TS_ASSERT(ui16 == 0x0304);
	ui16 = ENTOHSP(buffer + 4);
	TS_ASSERT(ui16 == 0x0405);
	ui16 = ENTOHSP(buffer + 5);
	TS_ASSERT(ui16 == 0x0506);
	ui16 = ENTOHSP(buffer + 6);
	TS_ASSERT(ui16 == 0x0607);
	ui16 = ENTOHSP(buffer + 7);
	TS_ASSERT(ui16 == 0x0708);
	ui16 = ENTOHSP(buffer + 8);
	TS_ASSERT(ui16 == 0x0809);
	ui16 = ENTOHSP(buffer + 9);
	TS_ASSERT(ui16 == 0x090a);
	ui16 = ENTOHSP(buffer + 10);
	TS_ASSERT(ui16 == 0x0a0b);
	ui16 = ENTOHSP(buffer + 11);
	TS_ASSERT(ui16 == 0x0b0c);
	ui16 = ENTOHSP(buffer + 12);
	TS_ASSERT(ui16 == 0x0c0d);
	ui16 = ENTOHSP(buffer + 13);
	TS_ASSERT(ui16 == 0x0d0e);
	ui16 = ENTOHSP(buffer + 14);
	TS_ASSERT(ui16 == 0x0e0f);

	ui32 = ENTOHLP(buffer);
	TS_ASSERT(ui32 == 0x00010203);
	ui32 = ENTOHLP(buffer + 1);
	TS_ASSERT(ui32 == 0x01020304);
	ui32 = ENTOHLP(buffer + 2);
	TS_ASSERT(ui32 == 0x02030405);
	ui32 = ENTOHLP(buffer + 3);
	TS_ASSERT(ui32 == 0x03040506);
	ui32 = ENTOHLP(buffer + 4);
	TS_ASSERT(ui32 == 0x04050607);
	ui32 = ENTOHLP(buffer + 5);
	TS_ASSERT(ui32 == 0x05060708);
	ui32 = ENTOHLP(buffer + 6);
	TS_ASSERT(ui32 == 0x06070809);
	ui32 = ENTOHLP(buffer + 7);
	TS_ASSERT(ui32 == 0x0708090a);
	ui32 = ENTOHLP(buffer + 8);
	TS_ASSERT(ui32 == 0x08090a0b);
	ui32 = ENTOHLP(buffer + 9);
	TS_ASSERT(ui32 == 0x090a0b0c);
	ui32 = ENTOHLP(buffer + 10);
	TS_ASSERT(ui32 == 0x0a0b0c0d);
	ui32 = ENTOHLP(buffer + 11);
	TS_ASSERT(ui32 == 0x0b0c0d0e);
	ui32 = ENTOHLP(buffer + 12);
	TS_ASSERT(ui32 == 0x0c0d0e0f);

	ui32 = ENTOHAP(buffer);
	TS_ASSERT(ui32 == 0x03000102);
	ui32 = ENTOHAP(buffer + 1);
	TS_ASSERT(ui32 == 0x04010203);
	ui32 = ENTOHAP(buffer + 2);
	TS_ASSERT(ui32 == 0x05020304);
	ui32 = ENTOHAP(buffer + 3);
	TS_ASSERT(ui32 == 0x06030405);
	ui32 = ENTOHAP(buffer + 4);
	TS_ASSERT(ui32 == 0x07040506);
	ui32 = ENTOHAP(buffer + 5);
	TS_ASSERT(ui32 == 0x08050607);
	ui32 = ENTOHAP(buffer + 6);
	TS_ASSERT(ui32 == 0x09060708);
	ui32 = ENTOHAP(buffer + 7);
	TS_ASSERT(ui32 == 0x0a070809);
	ui32 = ENTOHAP(buffer + 8);
	TS_ASSERT(ui32 == 0x0b08090a);
	ui32 = ENTOHAP(buffer + 9);
	TS_ASSERT(ui32 == 0x0c090a0b);
	ui32 = ENTOHAP(buffer + 10);
	TS_ASSERT(ui32 == 0x0d0a0b0c);
	ui32 = ENTOHAP(buffer + 11);
	TS_ASSERT(ui32 == 0x0e0b0c0d);
	ui32 = ENTOHAP(buffer + 12);
	TS_ASSERT(ui32 == 0x0f0c0d0e);

	ui64 = ENTOHLLP(buffer);
	TS_ASSERT(ui64 == 0x0001020304050607LL);
	ui64 = ENTOHLLP(buffer + 1);
	TS_ASSERT(ui64 == 0x0102030405060708LL);
	ui64 = ENTOHLLP(buffer + 2);
	TS_ASSERT(ui64 == 0x0203040506070809LL);
	ui64 = ENTOHLLP(buffer + 3);
	TS_ASSERT(ui64 == 0x030405060708090aLL);
	ui64 = ENTOHLLP(buffer + 4);
	TS_ASSERT(ui64 == 0x0405060708090a0bLL);
	ui64 = ENTOHLLP(buffer + 5);
	TS_ASSERT(ui64 == 0x05060708090a0b0cLL);
	ui64 = ENTOHLLP(buffer + 6);
	TS_ASSERT(ui64 == 0x060708090a0b0c0dLL);
	ui64 = ENTOHLLP(buffer + 7);
	TS_ASSERT(ui64 == 0x0708090a0b0c0d0eLL);
	ui64 = ENTOHLLP(buffer + 8);
	TS_ASSERT(ui64 == 0x08090a0b0c0d0e0fLL);

	char *pTempBuffer = new char[64 + 8];
	unsigned char rawDouble[] = {0x40, 0x5E, 0xDD, 0x2F, 0x1A, 0x9F, 0xBE, 0x77};
	double tempDoubleVal = 0;
	for (int i = 0; i <= 64; i++) {
		memset(pTempBuffer, 0, i);
		memcpy(pTempBuffer + i, rawDouble, 8);
		memset(pTempBuffer + i + 8, 0, 64 + 8 - i - 8);
		ENTOHDP((pTempBuffer + i), tempDoubleVal);
		TS_ASSERT(d == tempDoubleVal);
	}
	delete[] pTempBuffer;

	//network to host
#ifdef LITTLE_ENDIAN_BYTE_ALIGNED
	TS_ASSERT(ENTOHA(0x01040302) == 0x01020304);
	TS_ASSERT(ENTOHLL(0x0807060504030201LL) == 0x0102030405060708LL);
	ENTOHD(0x77BE9F1A2FDD5E40LL, tempDoubleVal);
	TS_ASSERT(d == tempDoubleVal);
#endif /* LITTLE_ENDIAN_BYTE_ALIGNED */

#ifdef LITTLE_ENDIAN_SHORT_ALIGNED
	TS_ASSERT(ENTOHA(0x01040302) == 0x01020304);
	TS_ASSERT(ENTOHLL(0x0807060504030201LL) == 0x0102030405060708LL);
	ENTOHD(0x77BE9F1A2FDD5E40LL, tempDoubleVal);
	TS_ASSERT(d == tempDoubleVal);
#endif /* LITTLE_ENDIAN_SHORT_ALIGNED */

#ifdef BIG_ENDIAN_BYTE_ALIGNED
	TS_ASSERT(ENTOHA(0x02030401) == 0x01020304);
	TS_ASSERT(ENTOHLL(0x0102030405060708LL) == 0x0102030405060708LL);
#error ENTOHD not tested
#endif /* BIG_ENDIAN_BYTE_ALIGNED */

#ifdef BIG_ENDIAN_SHORT_ALIGNED
#error BIG_ENDIAN_SHORT_ALIGNED set of tests not yet implemented!!! Please take care of this first!!!
#endif /* BIG_ENDIAN_SHORT_ALIGNED */

	//double mirror
	TS_ASSERT(ENTOHS(EHTONS(0x0102)) == 0x0102);
	TS_ASSERT(EHTONS(ENTOHS(0x0102)) == 0x0102);

	TS_ASSERT(ENTOHL(EHTONL(0x01020304)) == 0x01020304);
	TS_ASSERT(EHTONL(ENTOHL(0x01020304)) == 0x01020304);

	TS_ASSERT(ENTOHLL(EHTONLL(0x0102030405060708LL)) == 0x0102030405060708LL);
	TS_ASSERT(EHTONLL(ENTOHLL(0x0102030405060708LL)) == 0x0102030405060708LL);

	//EHTOND/ENTOHD are different. Requires 2 parameters. So, no double mirror

	TS_ASSERT(ENTOHA(EHTONA(0x01020304)) == 0x01020304);
	TS_ASSERT(EHTONA(ENTOHA(0x01020304)) == 0x01020304);

	// Buffer Put routines
	for (int i = 0; i < 16; i++) {
		EHTONSP(buffer + i, 0x0102);
		TS_ASSERT(ENTOHSP(buffer + i) == 0x0102);

		EHTONLP(buffer + i, 0x01020304);
		TS_ASSERT(ENTOHLP(buffer + i) == 0x01020304);

		EHTONLLP(buffer + i, 0x0102030405060708LL);
		TS_ASSERT(ENTOHLLP(buffer + i) == 0x0102030405060708LL);

		EHTONDP(d, (buffer + i));
		ENTOHDP(buffer + i, tempDoubleVal);
		TS_ASSERT(d == tempDoubleVal);
	}
}