コード例 #1
0
//
// Peek at the header and derive our total message length
//
static size_t
_messageLengthFromHeader(AthenaTransportLink *athenaTransportLink, _UDPLinkData *linkData)
{
    // Peek at our message header to determine the total length of buffer we need to allocate.
    size_t fixedHeaderLength = ccnxCodecTlvPacket_MinimalHeaderLength();
    PARCBuffer *wireFormatBuffer = parcBuffer_Allocate(fixedHeaderLength);
    const uint8_t *peekBuffer = parcBuffer_Overlay(wireFormatBuffer, 0);

    ssize_t readCount = recv(linkData->fd, (void *) peekBuffer, fixedHeaderLength, MSG_PEEK);

    if (readCount == -1) {
        parcBuffer_Release(&wireFormatBuffer);
        if ((errno == EAGAIN) || (errno == EINTR)) {
            parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "recv retry (%s)", strerror(errno));
            linkData->_stats.receive_ReadRetry++;
        } else {
            linkData->_stats.receive_ReadError++;
            parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "recv error (%s)", strerror(errno));
            athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Error);
        }
        return -1;
    }

    // A zero read means no data
    if (readCount == 0) {
        parcBuffer_Release(&wireFormatBuffer);
        return -1;
    }

    // Check for a short header read, since we're only peeking here we just return and retry later
    if (readCount != fixedHeaderLength) {
        linkData->_stats.receive_ReadHeaderFailure++;
        parcBuffer_Release(&wireFormatBuffer);
        return -1;
    }

    // Obtain the total size of the message from the header
    size_t messageLength = ccnxCodecTlvPacket_GetPacketLength(wireFormatBuffer);
    parcBuffer_Release(&wireFormatBuffer);

    // Could do more to check the integrity of the message and framing.
    // If length is greater than our MTU we will find out in the read.
    if (messageLength < fixedHeaderLength) {
        linkData->_stats.receive_BadMessageLength++;
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink), "Framing error, flushing link.");
        char trash[MAXPATHLEN];
        // Flush link to attempt to resync our framing
        while (read(linkData->fd, trash, sizeof(trash)) == sizeof(trash)) {
            parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink), "... flushing link.");
        }
        return -1;
    }

    return messageLength;
}
コード例 #2
0
AthenaTransportLink *
athenaTransportLinkModule_Open(AthenaTransportLinkModule *athenaTransportLinkModule, PARCURI *connectionURI)
{
    AthenaTransportLink *athenaTransportLink = athenaTransportLinkModule->openMethod(athenaTransportLinkModule, connectionURI);
    if (athenaTransportLink) {
        athenaTransportLink_SetAddLinkCallback(athenaTransportLink,
                                               (AthenaTransportLink_AddLinkCallback *) _athenaTransportLinkModule_AddLink,
                                               athenaTransportLinkModule);

        int result = _athenaTransportLinkModule_AddLink(athenaTransportLinkModule, athenaTransportLink);
        if (result == -1) {
            int addLinkError = errno;
            parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                          "Adding link %s failed: %s", athenaTransportLink_GetName(athenaTransportLink), strerror(errno));
            athenaTransportLink_Close(athenaTransportLink);
            errno = addLinkError;
            return NULL;
        }

        athenaTransportLink_SetRemoveLinkCallback(athenaTransportLink,
                                                  (AthenaTransportLink_RemoveLinkCallback *) _athenaTransportLinkModule_RemoveLink,
                                                  athenaTransportLinkModule);
    }
    return athenaTransportLink;
}
コード例 #3
0
static CCNxMetaMessage *
_TemplateReceive(AthenaTransportLink *athenaTransportLink)
{
    struct _TemplateLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink);
    CCNxMetaMessage *ccnxMetaMessage = NULL;

    PARCBuffer *wireFormatBuffer = _internalRECEIVE(linkData);

    // On error, just return and retry.
    if (wireFormatBuffer == NULL) {
        parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "read error (%s)", strerror(errno));
        return NULL;
    }

    // Construct, and return a ccnxMetaMessage from the wire format buffer.
    ccnxMetaMessage = ccnxMetaMessage_CreateFromWireFormatBuffer(wireFormatBuffer);
    if (ccnxMetaMessage == NULL) {
        linkData->_stats.receive_DecodeFailed++;
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink), "Failed to decode message from received packet.");
    }
    parcBuffer_Release(&wireFormatBuffer);

    if (parcDeque_Size(linkData->queue) > 0) { // if there's another message, mark an event.
        athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Receive);
    }
    return ccnxMetaMessage;
}
コード例 #4
0
ファイル: test_parc_Log.c プロジェクト: isolis/Libparc
LONGBOW_TEST_CASE(Global, parcLog_Error)
{
    PARCLog *log = (PARCLog *) longBowTestCase_GetClipBoardData(testCase);

    assertTrue(parcLog_Error(log, "This is a error message"),
               "Expected message to be logged successfully");
}
コード例 #5
0
ファイル: test_parc_Log.c プロジェクト: isolis/Libparc
LONGBOW_TEST_CASE(Global, parcLog_Error_WrongLevel)
{
    PARCLog *log = (PARCLog *) longBowTestCase_GetClipBoardData(testCase);
    parcLog_SetLevel(log, PARCLogLevel_Off);

    assertFalse(parcLog_Error(log, "This is a debug message"),
                "Expected message to not be logged");
}
コード例 #6
0
static AthenaTransportLink *
_newLink(AthenaTransportLink *athenaTransportLink, _UDPLinkData *newLinkData)
{
    // Accept a new tunnel connection.

    // Clone a new link from the current listener.
    const char *derivedLinkName = _createNameFromLinkData(&newLinkData->link);
    AthenaTransportLink *newTransportLink = athenaTransportLink_Clone(athenaTransportLink,
                                                                      derivedLinkName,
                                                                      _UDPSend,
                                                                      _UDPReceiveProxy,
                                                                      _UDPClose);
    if (newTransportLink == NULL) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "athenaTransportLink_Clone failed");
        parcMemory_Deallocate(&derivedLinkName);
        _UDPLinkData_Destroy(&newLinkData);
        return NULL;
    }

    _setConnectLinkState(newTransportLink, newLinkData);

    // Send the new link up to be added.
    int result = athenaTransportLink_AddLink(athenaTransportLink, newTransportLink);
    if (result == -1) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "athenaTransportLink_AddLink failed: %s", strerror(errno));
        _UDPLinkData_Destroy(&newLinkData);
        athenaTransportLink_Release(&newTransportLink);
    } else {
        parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink),
                     "new link accepted by %s: %s %s",
                     athenaTransportLink_GetName(athenaTransportLink), derivedLinkName,
                     athenaTransportLink_IsNotLocal(athenaTransportLink) ? "" : "(Local)");
    }

    parcMemory_Deallocate(&derivedLinkName);

    // Could pass a message back here regarding the new link.
    return newTransportLink;
}
コード例 #7
0
ファイル: athena_Ethernet.c プロジェクト: chris-wood/ghost
AthenaEthernet *
athenaEthernet_Create(PARCLog *log, const char *interface, uint16_t etherType)
{
    AthenaEthernet *athenaEthernet = parcObject_CreateAndClearInstance(AthenaEthernet);
    athenaEthernet->log = parcLog_Acquire(log);
    athenaEthernet->etherType = etherType;

    athenaEthernet->fd = _open_socket(interface);
    if (athenaEthernet->fd == -1) {
        parcLog_Error(athenaEthernet->log, "socket: %s", strerror(errno));

        athenaEthernet_Release(&athenaEthernet);
        return NULL;
    }

    if (ioctl(athenaEthernet->fd, BIOCGBLEN, &(athenaEthernet->etherBufferLength))) {
        perror("error getting buffer length");
        return NULL;
    }

    // Populate the configured physical MAC and MTU by searching ifaddrs
    struct ifaddrs *ifaddr;
    int res = getifaddrs(&ifaddr);
    if (res == -1) {
        perror("getifaddrs");
        return 0;
    }

    struct ifaddrs *next;
    for (next = ifaddr; next != NULL; next = next->ifa_next) {
        if (strcmp(next->ifa_name, interface) == 0) {
            if (next->ifa_addr->sa_family == AF_LINK) {
                struct sockaddr_dl *addr_dl = (struct sockaddr_dl *) next->ifa_addr;

                // addr_dl->sdl_data[12] contains the interface name followed by the MAC address, so
                // need to offset in to the array past the interface name.
                memcpy(&(athenaEthernet->mac), &addr_dl->sdl_data[addr_dl->sdl_nlen], addr_dl->sdl_alen);

                struct if_data *ifdata = (struct if_data *) next->ifa_data;
                athenaEthernet->mtu = ifdata->ifi_mtu;

                // break out of loop and freeifaddrs
                break;
            }
        }
    }
    freeifaddrs(ifaddr);

    return athenaEthernet;
}
コード例 #8
0
static int
_setSocketOptions(AthenaTransportLinkModule *athenaTransportLinkModule, int fd)
{
    int on = 1;
#ifdef BSD_IGNORESIGPIPE
    int result = setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void *) &on, sizeof(on));
    if (result) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "setsockopt failed to set SO_NOSIGPIPE (%s)", strerror(errno));
        return -1;
    }
#endif
    return 0;
}
コード例 #9
0
//
// Receive a message from the specified link.
//
static CCNxMetaMessage *
_ETHReceiveMessage(AthenaTransportLink *athenaTransportLink, struct ether_addr *peerAddress, socklen_t *peerAddressLength)
{
    struct _ETHLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink);
    CCNxMetaMessage *ccnxMetaMessage = NULL;
    AthenaTransportLinkEvent events = 0;

    PARCBuffer *message = athenaEthernet_Receive(linkData->athenaEthernet, -1, &events);
    if (message == NULL) {
        return NULL;
    }
    // Mark any pending events
    if (events) {
        athenaTransportLink_SetEvent(athenaTransportLink, events);
    }

    // Map the header
    struct ether_header *header = parcBuffer_Overlay(message, sizeof(struct ether_header));

    // If the destination does not match my address, drop the message
    if (memcmp(&linkData->link.myAddress, header->ether_dhost, ETHER_ADDR_LEN * sizeof(uint8_t)) != 0) {
        linkData->_stats.receive_NoLinkDestination++;
        parcBuffer_Release(&message);
        return NULL;
    }
    assertTrue(header->ether_type == htons(CCNX_ETHERTYPE), "Unexpected ether type %x", header->ether_type);

    // Set peerAddress from header source address
    *peerAddressLength = ETHER_ADDR_LEN * sizeof(uint8_t);
    memcpy(peerAddress, header->ether_shost, *peerAddressLength);

    parcBuffer_SetPosition(message, sizeof(struct ether_header));
    PARCBuffer *wireFormatBuffer = parcBuffer_Slice(message);
    parcBuffer_Release(&message);
    parcBuffer_SetPosition(wireFormatBuffer, 0);

    // Construct, and return a ccnxMetaMessage from the wire format buffer.
    ccnxMetaMessage = ccnxMetaMessage_CreateFromWireFormatBuffer(wireFormatBuffer);
    if (ccnxMetaMessage == NULL) {
        linkData->_stats.receive_DecodeFailed++;
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink), "Failed to decode message from received packet.");
    } else if (ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage) == CCNxTlvDictionary_SchemaVersion_V0) {
        parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink),
                      "received deprecated version %d message\n", ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage));
    }
    parcBuffer_Release(&wireFormatBuffer);

    return ccnxMetaMessage;
}
コード例 #10
0
ファイル: athena_Ethernet.c プロジェクト: chris-wood/ghost
PARCBuffer *
athenaEthernet_Receive(AthenaEthernet *athenaEthernet, int timeout, AthenaTransportLinkEvent *events)
{
    // Allocate, and read, a new BPF buffer if no packets are currently pending in an old one
    if (athenaEthernet->bpfBuffer == NULL) {
        athenaEthernet->bpfBuffer = parcBuffer_Allocate(athenaEthernet->etherBufferLength);
        uint8_t *buffer = parcBuffer_Overlay(athenaEthernet->bpfBuffer, 0);

        athenaEthernet->readCount = read(athenaEthernet->fd, buffer, athenaEthernet->etherBufferLength);
        if (athenaEthernet->readCount == -1) {
            if ((errno == EAGAIN) || (errno == EINTR)) {
                parcLog_Info(athenaEthernet->log, "Ethernet read retry");
                return NULL;
            }
            parcLog_Error(athenaEthernet->log, "recv: %s", strerror(errno));
            *events = AthenaTransportLinkEvent_Error;
            parcBuffer_Release(&athenaEthernet->bpfBuffer);
            return NULL;
        }
        parcLog_Debug(athenaEthernet->log, "received bpf packet (size=%d)", athenaEthernet->readCount);
    }

    // Obtain the current position in the BPF buffer to return a message from
    size_t position = parcBuffer_Position(athenaEthernet->bpfBuffer);

    // Read the BPF header and seek past it
    struct bpf_hdr *bpfhdr = parcBuffer_Overlay(athenaEthernet->bpfBuffer, sizeof(struct bpf_hdr));
    parcBuffer_SetLimit(athenaEthernet->bpfBuffer, position + bpfhdr->bh_hdrlen + bpfhdr->bh_datalen);
    parcBuffer_SetPosition(athenaEthernet->bpfBuffer, position + bpfhdr->bh_hdrlen);
    parcLog_Debug(athenaEthernet->log, "received message (size=%d)", bpfhdr->bh_datalen);

    // Slice a new PARCBuffer with the message to send up.
    PARCBuffer *wireFormatBuffer = parcBuffer_Slice(athenaEthernet->bpfBuffer);

    // If there's another packet in the buffer, position it and flag a receive event
    if ((athenaEthernet->readCount - (position + bpfhdr->bh_hdrlen + bpfhdr->bh_datalen)) != 0) {
        parcBuffer_SetLimit(athenaEthernet->bpfBuffer, athenaEthernet->readCount);
        parcBuffer_SetPosition(athenaEthernet->bpfBuffer,
                               BPF_WORDALIGN(position + bpfhdr->bh_hdrlen + bpfhdr->bh_datalen));
        // Mark a receive event for this packet
        *events = AthenaTransportLinkEvent_Receive;
    } else {
        parcBuffer_Release(&athenaEthernet->bpfBuffer);
    }

    return wireFormatBuffer;
}
コード例 #11
0
static bool
_linkIsEOF(AthenaTransportLink *athenaTransportLink)
{
    struct _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink);

    // If poll indicates there's a read event and a subsequent read returns zero our peer has hungup.
    struct pollfd pollfd = { .fd = linkData->fd, .events = POLLIN };
    int events = poll(&pollfd, 1, 0);
    if (events == -1) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink), "poll error (%s)", strerror(errno));
        return true; // poll error, close the link
    } else if (events == 0) {
        // there are no pending events, was truly a zero read
        return false;
    }
    if (pollfd.revents & POLLIN) {
        char peekBuffer;
        ssize_t readCount = recv(linkData->fd, (void *) &peekBuffer, 1, MSG_PEEK);
        if (readCount == -1) {
            if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { // read blocked
                linkData->_stats.receive_ReadWouldBlock++;
                return false;
            }
            return true; // read error
        }
        if (readCount == 0) { // EOF
            return true;
        }
    }
    return false;
}

static void
_UDPClose(AthenaTransportLink *athenaTransportLink)
{
    parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink),
                 "link %s closed", athenaTransportLink_GetName(athenaTransportLink));
    _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink);
    close(linkData->fd);
    _UDPLinkData_Destroy(&linkData);
}
コード例 #12
0
ファイル: athena_InterestControl.c プロジェクト: PARC/Athena
static CCNxMetaMessage *
_Control_Command_Spawn(Athena *athena, CCNxName *ccnxName, const char *command, const char *connectionSpecification)
{
    CCNxMetaMessage *responseMessage;

    // Create a new athena instance
    Athena *newAthena = athena_Create(AthenaDefaultContentStoreSize);
    if (newAthena == NULL) {
        responseMessage = _create_response(athena, ccnxName, "Could not create a new Athena instance");
        return responseMessage;
    }

    // Add the specified link
    PARCURI *connectionURI = parcURI_Parse(connectionSpecification);
    if (athenaTransportLinkAdapter_Open(newAthena->athenaTransportLinkAdapter, connectionURI) == NULL) {
        parcLog_Error(athena->log, "Unable to configure an interface.  Exiting...");
        responseMessage = _create_response(athena, ccnxName, "Unable to configure an Athena interface for thread");
        parcURI_Release(&connectionURI);
        athena_Release(&newAthena);
        return responseMessage;
    }
    parcURI_Release(&connectionURI);

    pthread_t thread;
    // Passing in a reference that will be released by the new thread as the thread may not
    // have time to acquire a reference itself before we release our reference.
    if (pthread_create(&thread, NULL, _start_forwarder_instance, (void *) athena_Acquire(newAthena)) != 0) {
        responseMessage = _create_response(athena, ccnxName, "Athena process thread creation failed");
        return responseMessage;
    }
    athena_Release(&newAthena);

    athenaInterestControl_LogConfigurationChange(athena, ccnxName, "%s", connectionSpecification);

    responseMessage = _create_response(athena, ccnxName, "Athena process thread started on %s", connectionSpecification);
    return responseMessage;
}
コード例 #13
0
int
athenaTransportLinkAdapter_Poll(AthenaTransportLinkAdapter *athenaTransportLinkAdapter, int timeout)
{
    struct pollfd *pollfdReceiveList = athenaTransportLinkAdapter->pollfdReceiveList;
    struct pollfd *pollfdSendList = athenaTransportLinkAdapter->pollfdSendList;
    int pollfdListSize = athenaTransportLinkAdapter->pollfdListSize;
    AthenaTransportLinkModule *athenaTransportLinkModule;
    int events = 0;

    // Allow instances which have not registered an eventfd to mark their events
    if (athenaTransportLinkAdapter->moduleList) {
        for (int index = 0; index < parcArrayList_Size(athenaTransportLinkAdapter->moduleList); index++) {
            athenaTransportLinkModule = parcArrayList_Get(athenaTransportLinkAdapter->moduleList, index);
            events += athenaTransportLinkModule_Poll(athenaTransportLinkModule, timeout);
        }
    }

    if (events) { // if we have existing events, poll doesn't need to block
        timeout = 0;
    }

    int result = poll(pollfdReceiveList, pollfdListSize, timeout);
    if (result < 0) {
        parcLog_Error(athenaTransportLinkAdapter_GetLogger(athenaTransportLinkAdapter),
                      "Receive list poll error: (%d) %s", errno, strerror(errno));
    } else {
        for (int index = 0; index < pollfdListSize; index++) {
            if (pollfdReceiveList[index].revents) {
                AthenaTransportLink *athenaTransportLink = athenaTransportLinkAdapter->pollfdTransportLink[index];
                if (athenaTransportLink) {
                    if (pollfdReceiveList[index].revents & (POLLERR | POLLHUP)) {
                        athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Error);
                        athenaTransportLink_Close(athenaTransportLink);
                    }
                    if (pollfdReceiveList[index].revents & POLLIN) {
                        athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Receive);
                    } else {
                        athenaTransportLink_ClearEvent(athenaTransportLink, AthenaTransportLinkEvent_Receive);
                    }
                }
            }
        }
    }

    result = poll(pollfdSendList, pollfdListSize, 0);
    if (result < 0) {
        parcLog_Error(athenaTransportLinkAdapter_GetLogger(athenaTransportLinkAdapter),
                      "Send list poll error: (%d) %s", errno, strerror(errno));
    } else {
        for (int index = 0; index < pollfdListSize; index++) {
            if (pollfdSendList[index].revents) {
                AthenaTransportLink *athenaTransportLink = athenaTransportLinkAdapter->pollfdTransportLink[index];
                if (athenaTransportLink) {
                    if (pollfdSendList[index].revents & (POLLNVAL | POLLHUP | POLLERR)) {
                        continue;
                    }
                    if (pollfdSendList[index].revents & (POLLERR | POLLHUP)) {
                        athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Error);
                        athenaTransportLink_Close(athenaTransportLink);
                    }
                    if (pollfdSendList[index].revents & POLLOUT) {
                        athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Send);
                    } else {
                        athenaTransportLink_ClearEvent(athenaTransportLink, AthenaTransportLinkEvent_Send);
                    }
                }
            }
        }
        //events += result; // don't register send events
    }
    return events;
}
コード例 #14
0
static AthenaTransportLink *
_UDPOpen(AthenaTransportLinkModule *athenaTransportLinkModule, PARCURI *connectionURI)
{
    AthenaTransportLink *result = 0;

    const char *authorityString = parcURI_GetAuthority(connectionURI);
    if (authorityString == NULL) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unable to parse connection authority %s", authorityString);
        errno = EINVAL;
        return NULL;
    }
    PARCURIAuthority *authority = parcURIAuthority_Parse(authorityString);
    const char *URIAddress = parcURIAuthority_GetHostName(authority);
    in_port_t port = parcURIAuthority_GetPort(authority);

    // Normalize the provided hostname
    struct sockaddr_in *addr = (struct sockaddr_in *) parcNetwork_SockAddress(URIAddress, port);
    char *address = inet_ntoa(addr->sin_addr);
    parcMemory_Deallocate(&addr);

    parcURIAuthority_Release(&authority);

    if (address == NULL) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unable to lookup hostname %s", address);
        errno = EINVAL;
        return NULL;
    }
    if (port == 0) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Invalid address specification, port == 0");
        errno = EINVAL;
        return NULL;
    }

    bool listener = false;
    char name[MAXPATHLEN] = { 0 };
    char srcAddress[NI_MAXHOST] = "0.0.0.0";
    size_t mtu = 0;
    uint16_t srcPort = 0;
    char localFlag[MAXPATHLEN] = { 0 };
    int forceLocal = 0;
    char *linkName = NULL;

    PARCURIPath *remainder = parcURI_GetPath(connectionURI);
    size_t segments = parcURIPath_Count(remainder);
    for (int i = 0; i < segments; i++) {
        PARCURISegment *segment = parcURIPath_Get(remainder, i);
        const char *token = parcURISegment_ToString(segment);

        if (strcasecmp(token, UDP_LISTENER_FLAG) == 0) {
            listener = true;
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, SRC_LINK_SPECIFIER, strlen(SRC_LINK_SPECIFIER)) == 0) {
            if (sscanf(token, "%*[^%%]%%3D%[^%%]%%3A%hd", srcAddress, &srcPort) != 2) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper connection source specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            // Normalize the provided hostname
            struct sockaddr_in *addr = (struct sockaddr_in *) parcNetwork_SockAddress(srcAddress, srcPort);
            char *hostname = inet_ntoa(addr->sin_addr);
            parcMemory_Deallocate(&addr);

            memcpy(srcAddress, hostname, strlen(hostname) + 1);
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, LINK_MTU_SIZE, strlen(LINK_MTU_SIZE)) == 0) {
            if (sscanf(token, "%*[^%%]%%3D%zd", &mtu) != 1) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper MTU specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, LINK_NAME_SPECIFIER, strlen(LINK_NAME_SPECIFIER)) == 0) {
            if (sscanf(token, "%*[^%%]%%3D%s", name) != 1) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper connection name specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            linkName = name;
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, LOCAL_LINK_FLAG, strlen(LOCAL_LINK_FLAG)) == 0) {
            if (sscanf(token, "%*[^%%]%%3D%s", localFlag) != 1) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper local specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            if (strncasecmp(localFlag, "false", strlen("false")) == 0) {
                forceLocal = AthenaTransportLink_ForcedNonLocal;
            } else if (strncasecmp(localFlag, "true", strlen("true")) == 0) {
                forceLocal = AthenaTransportLink_ForcedLocal;
            } else {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper local state specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            parcMemory_Deallocate(&token);
            continue;
        }

        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unknown connection parameter (%s)", token);
        parcMemory_Deallocate(&token);
        errno = EINVAL;
        return NULL;
    }

    struct sockaddr_in *destination = parcNetwork_SockInet4Address(address, port);
    struct sockaddr_in *source = parcNetwork_SockInet4Address(srcAddress, srcPort);

    if (listener) {
        result = _UDPOpenListener(athenaTransportLinkModule, linkName, destination, mtu);
    } else {
        result = _UDPOpenConnection(athenaTransportLinkModule, linkName, source, destination, mtu);
    }

    parcMemory_Deallocate(&destination);
    parcMemory_Deallocate(&source);

    // forced IsLocal/IsNotLocal, mainly for testing
    if (result && forceLocal) {
        athenaTransportLink_ForceLocal(result, forceLocal);
    }

    return result;
}
コード例 #15
0
//
// Open a listener which will create new links when messages arrive and queue them appropriately.
// Listeners are inherently insecure, as an adversary could easily create many connections that are never closed.
//
static AthenaTransportLink *
_UDPOpenListener(AthenaTransportLinkModule *athenaTransportLinkModule, const char *linkName, struct sockaddr_in *destination, size_t mtu)
{
    const char *derivedLinkName;

    _UDPLinkData *linkData = _UDPLinkData_Create();
    linkData->multiplexTable = parcHashCodeTable_Create(_connectionEquals, _connectionHashCode, NULL, _closeConnection);
    assertNotNull(linkData->multiplexTable, "Could not create multiplex table for new listener");

    linkData->link.myAddress = *destination;
    linkData->link.myAddressLength = sizeof(struct sockaddr_in);
    linkData->link.mtu = mtu;

    linkData->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (linkData->fd < 0) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "socket error (%s)", strerror(errno));
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    int result = _setSocketOptions(athenaTransportLinkModule, linkData->fd);
    if (result) {
        close(linkData->fd);
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    // Set non-blocking flag
    int flags = fcntl(linkData->fd, F_GETFL, NULL);
    if (flags < 0) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "fcntl failed to get non-blocking flag (%s)", strerror(errno));
        close(linkData->fd);
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }
    result = fcntl(linkData->fd, F_SETFL, flags | O_NONBLOCK);
    if (result) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "fcntl failed to set non-blocking flag (%s)", strerror(errno));
        close(linkData->fd);
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    // bind to listen on requested address
    result = bind(linkData->fd, (struct sockaddr *) &linkData->link.myAddress, linkData->link.myAddressLength);
    if (result) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "bind error (%s)", strerror(errno));
        close(linkData->fd);
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    derivedLinkName = _createNameFromLinkData(&linkData->link);

    if (linkName == NULL) {
        linkName = derivedLinkName;
    }

    // Listener doesn't require a send method.  The receive method is used to establish new connections.
    AthenaTransportLink *athenaTransportLink = athenaTransportLink_Create(linkName,
                                                                          NULL,
                                                                          _UDPReceiveListener,
                                                                          _UDPClose);
    if (athenaTransportLink == NULL) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "athenaTransportLink_Create failed");
        parcMemory_Deallocate(&derivedLinkName);
        close(linkData->fd);
        _UDPLinkData_Destroy(&linkData);
        return athenaTransportLink;
    }

    athenaTransportLink_SetPrivateData(athenaTransportLink, linkData);
    athenaTransportLink_SetEventFd(athenaTransportLink, linkData->fd);

    // Links established for listening are not used to route messages.
    // They can be kept in a listener list that doesn't consume a linkId.
    athenaTransportLink_SetRoutable(athenaTransportLink, false);

    parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink),
                 "new listener established: Name=\"%s\" (%s)", linkName, derivedLinkName);

    parcMemory_Deallocate(&derivedLinkName);
    return athenaTransportLink;
}
コード例 #16
0
//
// Open a UDP point to point connection.
//
static AthenaTransportLink *
_UDPOpenConnection(AthenaTransportLinkModule *athenaTransportLinkModule, const char *linkName, struct sockaddr_in *source, struct sockaddr_in *destination, size_t mtu)
{
    const char *derivedLinkName;

    _UDPLinkData *linkData = _UDPLinkData_Create();

    linkData->link.peerAddress = *((struct sockaddr_in *) destination);
    linkData->link.peerAddressLength = sizeof(struct sockaddr_in);
    linkData->link.mtu = mtu;

    linkData->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (linkData->fd < 0) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule), "socket error (%s)", strerror(errno));
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    int result = _setSocketOptions(athenaTransportLinkModule, linkData->fd);
    if (result) {
        close(linkData->fd);
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    // bind the local endpoint so we can know our allocated port if it was wildcarded
    result = bind(linkData->fd, (struct sockaddr *) source, sizeof(struct sockaddr_in));
    if (result) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "bind error (%s)", strerror(errno));
        close(linkData->fd);
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    // Retrieve the local endpoint data, used to create the derived name.
    linkData->link.myAddressLength = sizeof(struct sockaddr_in);
    result = getsockname(linkData->fd, (struct sockaddr *) &linkData->link.myAddress, &linkData->link.myAddressLength);
    if (result != 0) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Failed to obtain endpoint information from getsockname.");
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    derivedLinkName = _createNameFromLinkData(&linkData->link);

    if (linkName == NULL) {
        linkName = derivedLinkName;
    }

    AthenaTransportLink *athenaTransportLink = athenaTransportLink_Create(linkName,
                                                                          _UDPSend,
                                                                          _UDPReceive,
                                                                          _UDPClose);
    if (athenaTransportLink == NULL) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "athenaTransportLink_Create failed");
        parcMemory_Deallocate(&derivedLinkName);
        _UDPLinkData_Destroy(&linkData);
        return NULL;
    }

    _setConnectLinkState(athenaTransportLink, linkData);
    // Enable Send? XXX
    athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Send);

    parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink),
                 "new link established: Name=\"%s\" (%s)", linkName, derivedLinkName);

    parcMemory_Deallocate(&derivedLinkName);
    return athenaTransportLink;
    return NULL;
}
コード例 #17
0
//
// Receive a message from the specified link.
//
static CCNxMetaMessage *
_UDPReceiveMessage(AthenaTransportLink *athenaTransportLink, struct sockaddr_in *peerAddress, socklen_t *peerAddressLength)
{
    struct _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink);
    CCNxMetaMessage *ccnxMetaMessage = NULL;
    size_t messageLength;

    // If an MTU has been set, allocate a buffer of that size to avoid having to peek at the message,
    // othersize derive the link from the header and allocate a buffer based on the message size.

    if ((messageLength = linkData->link.mtu) == 0) {
        messageLength = _messageLengthFromHeader(athenaTransportLink, linkData);
        if (messageLength <= 0) {
            return NULL;
        }
    }

    PARCBuffer *wireFormatBuffer = parcBuffer_Allocate(messageLength);

    char *buffer = parcBuffer_Overlay(wireFormatBuffer, 0);
    *peerAddressLength = (socklen_t) sizeof(struct sockaddr_in);
    ssize_t readCount = recvfrom(linkData->fd, buffer, messageLength, 0, (struct sockaddr *) peerAddress, peerAddressLength);

    // On error, just return and retry.
    if (readCount == -1) {
        linkData->_stats.receive_ReadError++;
        parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "read error (%s)", strerror(errno));
        parcBuffer_Release(&wireFormatBuffer);
        return NULL;
    }

    // A zero read means either no more data is currently available or our peer hungup.
    // Just return to retry as we'll detect EOF when we come back at the top of UDPReceive
    if (readCount == 0) {
        parcBuffer_Release(&wireFormatBuffer);
        return NULL;
    }

    // If it was it a short read just return to retry later.
    while (readCount < messageLength) {
        linkData->_stats.receive_ShortRead++;
        parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "short read error (%s)", strerror(errno));
        parcBuffer_Release(&wireFormatBuffer);
        return NULL;
    }

    parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "received message (size=%d)", readCount);
    parcBuffer_SetPosition(wireFormatBuffer, parcBuffer_Position(wireFormatBuffer) + readCount);
    parcBuffer_Flip(wireFormatBuffer);

    // Construct, and return a ccnxMetaMessage from the wire format buffer.
    ccnxMetaMessage = ccnxMetaMessage_CreateFromWireFormatBuffer(wireFormatBuffer);
    if (ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage) == CCNxTlvDictionary_SchemaVersion_V0) {
        parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink),
                      "received deprecated version %d message\n", ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage));
    }
    if (ccnxMetaMessage == NULL) {
        linkData->_stats.receive_DecodeFailed++;
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink), "Failed to decode message from received packet.");
    }
    parcBuffer_Release(&wireFormatBuffer);

    return ccnxMetaMessage;
}
コード例 #18
0
//
// Open a point to point connection.
//
static AthenaTransportLink *
_ETHOpenConnection(AthenaTransportLinkModule *athenaTransportLinkModule, const char *linkName, const char *device, struct ether_addr *source, struct ether_addr *destination, size_t mtu)
{
    const char *derivedLinkName;

    _ETHLinkData *linkData = _ETHLinkData_Create();

    linkData->athenaEthernet = athenaEthernet_Create(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                                                     device, CCNX_ETHERTYPE);
    if (linkData->athenaEthernet == NULL) {
        _ETHLinkData_Destroy(&linkData);
        return NULL;
    }

    // Use our default MAC address if none specified.
    if (source == NULL) {
        athenaEthernet_GetMAC(linkData->athenaEthernet, &(linkData->link.myAddress));
        linkData->link.myAddressLength = ETHER_ADDR_LEN;
    } else {
        memcpy(&(linkData->link.myAddress), source, sizeof(struct ether_addr));
    }

    // If there's no destination specified, drop the request.
    if (destination == NULL) {
        _ETHLinkData_Destroy(&linkData);
        return NULL;
    }

    // Copy the peer destination address into our link data
    memcpy(&(linkData->link.peerAddress), destination, sizeof(struct ether_addr));

    derivedLinkName = _createNameFromLinkData(&linkData->link, false);

    if (linkName == NULL) {
        linkName = derivedLinkName;
    }

    AthenaTransportLink *athenaTransportLink = athenaTransportLink_Create(linkName,
                                                                          _ETHSend,
                                                                          _ETHReceive,
                                                                          _ETHClose);
    if (athenaTransportLink == NULL) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "athenaTransportLink_Create failed");
        parcMemory_Deallocate(&derivedLinkName);
        _ETHLinkData_Destroy(&linkData);
        return NULL;
    }

    _setConnectLinkState(athenaTransportLink, linkData);

    // Enable Sends
    athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Send);

    parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink),
                 "new link established: Name=\"%s\" (%s)", linkName, derivedLinkName);

    parcMemory_Deallocate(&derivedLinkName);
    return athenaTransportLink;
    return NULL;
}
コード例 #19
0
static int
_UDPSend(AthenaTransportLink *athenaTransportLink, CCNxMetaMessage *ccnxMetaMessage)
{
    struct _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink);

    if (ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage) == CCNxTlvDictionary_SchemaVersion_V0) {
        parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink),
                      "sending deprecated version %d message\n", ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage));
    }

    // Get a wire format buffer and write it out.
    PARCBuffer *wireFormatBuffer = ccnxWireFormatMessage_GetWireFormatBuffer(ccnxMetaMessage);

    if (wireFormatBuffer == NULL) {
        CCNxCodecNetworkBufferIoVec *iovec = ccnxWireFormatMessage_GetIoVec(ccnxMetaMessage);
        assertNotNull(iovec, "Null io vector");

        size_t iovcnt = ccnxCodecNetworkBufferIoVec_GetCount((CCNxCodecNetworkBufferIoVec *) iovec);
        const struct iovec *array = ccnxCodecNetworkBufferIoVec_GetArray((CCNxCodecNetworkBufferIoVec *) iovec);

        // If it's a single vector wrap it in a buffer to avoid a copy
        if (iovcnt == 1) {
            wireFormatBuffer = parcBuffer_Wrap(array[0].iov_base, array[0].iov_len, 0, array[0].iov_len);
        } else {
            size_t totalbytes = 0;
            for (int i = 0; i < iovcnt; i++) {
                totalbytes += array[i].iov_len;
            }
            wireFormatBuffer = parcBuffer_Allocate(totalbytes);
            for (int i = 0; i < iovcnt; i++) {
                parcBuffer_PutArray(wireFormatBuffer, array[i].iov_len, array[i].iov_base);
            }
            parcBuffer_Flip(wireFormatBuffer);
        }
    } else {
        wireFormatBuffer = parcBuffer_Acquire(wireFormatBuffer);
    }

    size_t length = parcBuffer_Limit(wireFormatBuffer);
    char *buffer = parcBuffer_Overlay(wireFormatBuffer, length);

    if (linkData->link.mtu) {
        if (length > linkData->link.mtu) {
            errno = EMSGSIZE;
            parcBuffer_Release(&wireFormatBuffer);
            return -1;
        }
    }

    parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink),
                  "sending message (size=%d)", length);

    ssize_t writeCount = 0;
#ifdef LINUX_IGNORESIGPIPE
    writeCount = sendto(linkData->fd, buffer, length, MSG_NOSIGNAL,
                        (struct sockaddr *) &linkData->link.peerAddress, linkData->link.peerAddressLength);
#else
    writeCount = sendto(linkData->fd, buffer, length, 0,
                        (struct sockaddr *) &linkData->link.peerAddress, linkData->link.peerAddressLength);
#endif

    // on error close the link, else return to retry a zero write
    if (writeCount == -1) {
        if (errno == EPIPE) {
            athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Error);
        }
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "send error (%s)", strerror(errno));
        parcBuffer_Release(&wireFormatBuffer);
        return -1;
    }

    // Short write
    if (writeCount != length) {
        linkData->_stats.receive_ShortWrite++;
        parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "short write");
        parcBuffer_Release(&wireFormatBuffer);
        return -1;
    }

    parcBuffer_Release(&wireFormatBuffer);
    return 0;
}
コード例 #20
0
static int
_UDPSend(AthenaTransportLink *athenaTransportLink, CCNxMetaMessage *ccnxMetaMessage)
{
    struct _UDPLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink);

    if (ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage) == CCNxTlvDictionary_SchemaVersion_V0) {
        parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink),
                      "sending deprecated version %d message\n", ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage));
    }

    // Get a wire format buffer and write it out.
    PARCBuffer *wireFormatBuffer = athenaTransportLinkModule_GetMessageBuffer(ccnxMetaMessage);

    parcBuffer_SetPosition(wireFormatBuffer, 0);
    size_t length = parcBuffer_Limit(wireFormatBuffer);
    char *buffer = parcBuffer_Overlay(wireFormatBuffer, length);

    if (linkData->link.mtu) {
        if (length > linkData->link.mtu) {
            errno = EMSGSIZE;
            parcBuffer_Release(&wireFormatBuffer);
            return -1;
        }
    }

    parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink),
                  "sending message (size=%d)", length);

    ssize_t writeCount = 0;
#ifdef LINUX_IGNORESIGPIPE
    writeCount = sendto(linkData->fd, buffer, length, MSG_NOSIGNAL,
                        (struct sockaddr *) &linkData->link.peerAddress, linkData->link.peerAddressLength);
#else
    writeCount = sendto(linkData->fd, buffer, length, 0,
                        (struct sockaddr *) &linkData->link.peerAddress, linkData->link.peerAddressLength);
#endif

    // on error close the link, else return to retry a zero write
    if (writeCount == -1) {
        if ((errno == EAGAIN) || (errno == EINTR)) {
            linkData->_stats.send_SendRetry++;
            parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "send retry (%s)", strerror(errno));
        } else {
            athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Error);
            parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                          "send error (%s)", strerror(errno));
        }
        parcBuffer_Release(&wireFormatBuffer);
        return -1;
    }

    // Short write
    if (writeCount != length) {
        linkData->_stats.send_ShortWrite++;
        parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "short write");
        parcBuffer_Release(&wireFormatBuffer);
        return -1;
    }

    parcBuffer_Release(&wireFormatBuffer);
    return 0;
}
コード例 #21
0
static AthenaTransportLink *
_TemplateOpen(AthenaTransportLinkModule *athenaTransportLinkModule, PARCURI *connectionURI)
{
    // Parse the URI contents to determine the link specific parameters
    const char *authorityString = parcURI_GetAuthority(connectionURI);
    if (authorityString == NULL) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unable to parse connection authority %s", authorityString);
        errno = EINVAL;
        return NULL;
    }

    //
    // This template link module doesn't use the authority fields.
    // The access methods are here for use by derived link modules, if needed.
    //
    PARCURIAuthority *authority = parcURIAuthority_Parse(authorityString);
    //const char *URIAddress = parcURIAuthority_GetHostName(authority);
    //in_port_t port = parcURIAuthority_GetPort(authority);
    parcURIAuthority_Release(&authority);

    int forceLocal = 0;
    char specifiedLinkName[MAXPATHLEN] = { 0 };
    const char *linkName = NULL;

    // Parse connection segment parameters, Name and Local
    PARCURIPath *remainder = parcURI_GetPath(connectionURI);
    size_t segments = parcURIPath_Count(remainder);
    for (int i = 0; i < segments; i++) {
        PARCURISegment *segment = parcURIPath_Get(remainder, i);
        const char *token = parcURISegment_ToString(segment);

        if (strncasecmp(token, LINK_NAME_SPECIFIER, strlen(LINK_NAME_SPECIFIER)) == 0) {
            if (_parseLinkName(token, specifiedLinkName) != 0) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper connection name specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            linkName = specifiedLinkName;
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, LOCAL_LINK_FLAG, strlen(LOCAL_LINK_FLAG)) == 0) {
            forceLocal = _parseLocalFlag(token);
            if (forceLocal == 0) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper local specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            parcMemory_Deallocate(&token);
            continue;
        }

        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unknown connection parameter (%s)", token);
        parcMemory_Deallocate(&token);
        errno = EINVAL;
        return NULL;
    }

    _TemplateLinkData *linkData = _TemplateLinkData_Create();

    const char *derivedLinkName = _createNameFromLinkData(linkData);

    if (linkName == NULL) {
        linkName = derivedLinkName;
    }

    AthenaTransportLink *athenaTransportLink = athenaTransportLink_Create(linkName,
                                                                          _TemplateSend,
                                                                          _TemplateReceive,
                                                                          _TemplateClose);
    if (athenaTransportLink == NULL) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "athenaTransportLink_Create failed");
        parcMemory_Deallocate(&derivedLinkName);
        _TemplateLinkData_Destroy(&linkData);
        return athenaTransportLink;
    }

    athenaTransportLink_SetPrivateData(athenaTransportLink, linkData);
    athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Send);

    parcLog_Info(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                 "new link established: Name=\"%s\" (%s)", linkName, derivedLinkName);

    parcMemory_Deallocate(&derivedLinkName);

    // forced IsLocal/IsNotLocal, mainly for testing
    if (athenaTransportLink && forceLocal) {
        athenaTransportLink_ForceLocal(athenaTransportLink, forceLocal);
    }

    return athenaTransportLink;
}
コード例 #22
0
//
// Open a listener which will create new links when messages arrive and queue them appropriately.
// Listeners are inherently insecure, as an adversary could easily create many connections that are never closed.
//
static AthenaTransportLink *
_ETHOpenListener(AthenaTransportLinkModule *athenaTransportLinkModule, const char *linkName, const char *device, struct ether_addr *source, size_t mtu)
{
    const char *derivedLinkName;

    _ETHLinkData *linkData = _ETHLinkData_Create();
    linkData->multiplexTable = parcHashCodeTable_Create(_connectionEquals, _connectionHashCode, NULL, _closeConnection);
    assertNotNull(linkData->multiplexTable, "Could not create multiplex table for new listener");

    linkData->athenaEthernet = athenaEthernet_Create(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                                                     device, CCNX_ETHERTYPE);
    if (linkData->athenaEthernet == NULL) {
        _ETHLinkData_Destroy(&linkData);
        return NULL;
    }

    // Use specified source MAC address, or default to device MAC
    if (source) {
        memcpy(&(linkData->link.myAddress), source, sizeof(struct ether_addr));
    } else {
        athenaEthernet_GetMAC(linkData->athenaEthernet, &(linkData->link.myAddress));
    }
    linkData->link.myAddressLength = ETHER_ADDR_LEN;

    if (linkData->athenaEthernet == NULL) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "athenaEthernet_Create error");
        _ETHLinkData_Destroy(&linkData);
        return NULL;
    }

    derivedLinkName = _createNameFromLinkData(&linkData->link, true);

    if (linkName == NULL) {
        linkName = derivedLinkName;
    }

    // Listener doesn't require a send method.  The receive method is used to establish new connections.
    AthenaTransportLink *athenaTransportLink = athenaTransportLink_Create(linkName,
                                                                          NULL,
                                                                          _ETHReceiveListener,
                                                                          _ETHClose);
    if (athenaTransportLink == NULL) {
        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "athenaTransportLink_Create failed");
        parcMemory_Deallocate(&derivedLinkName);
        _ETHLinkData_Destroy(&linkData);
        return athenaTransportLink;
    }

    athenaTransportLink_SetPrivateData(athenaTransportLink, linkData);
    athenaTransportLink_SetEventFd(athenaTransportLink, athenaEthernet_GetDescriptor(linkData->athenaEthernet));

    // Links established for listening are not used to route messages.
    // They can be kept in a listener list that doesn't consume a linkId.
    athenaTransportLink_SetRoutable(athenaTransportLink, false);

    parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink),
                 "new listener established: Name=\"%s\" (%s)", linkName, derivedLinkName);

    parcMemory_Deallocate(&derivedLinkName);
    return athenaTransportLink;
}
コード例 #23
0
static AthenaTransportLink *
_ETHOpen(AthenaTransportLinkModule *athenaTransportLinkModule, PARCURI *connectionURI)
{
    AthenaTransportLink *result = 0;
    char device[NI_MAXHOST];

    const char *authorityString = parcURI_GetAuthority(connectionURI);
    if (authorityString == NULL) {
        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unable to parse connection authority %s", authorityString);
        errno = EINVAL;
        return NULL;
    }
    PARCURIAuthority *authority = parcURIAuthority_Parse(authorityString);
    const char *URIHostname = parcURIAuthority_GetHostName(authority);
    strcpy(device, URIHostname);

    bool srcMACSpecified = false;
    struct ether_addr srcMAC = { { 0 } };
    struct ether_addr destMAC = { { 0 } };
    if (_parseAddress(authorityString, &destMAC) != 0) {
        parcURIAuthority_Release(&authority);
        errno = EINVAL;
        return NULL;
    }
    parcURIAuthority_Release(&authority);

    bool isListener = false;
    char *linkName = NULL;
    char specifiedLinkName[MAXPATHLEN] = { 0 };
    size_t mtu = 0;
    int forceLocal = 0;

    PARCURIPath *remainder = parcURI_GetPath(connectionURI);
    size_t segments = parcURIPath_Count(remainder);
    for (int i = 0; i < segments; i++) {
        PARCURISegment *segment = parcURIPath_Get(remainder, i);
        const char *token = parcURISegment_ToString(segment);

        if (strcasecmp(token, ETH_LISTENER_FLAG) == 0) {
            // Packet source for listener is destination parameter, unless told otherwise
            if (srcMACSpecified == false) {
                memcpy(&srcMAC, &destMAC, sizeof(struct ether_addr));
            }
            isListener = true;
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, SRC_LINK_SPECIFIER, strlen(SRC_LINK_SPECIFIER)) == 0) {
            if (_parseSrc(athenaTransportLinkModule, token, &srcMAC) != 0) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper connection source specification (%s)", token);
                parcMemory_Deallocate(&token);
                return NULL;
            }
            srcMACSpecified = true;
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, LINK_MTU_SIZE, strlen(LINK_MTU_SIZE)) == 0) {
            if (_parseMTU(token, &mtu) == -1) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper MTU specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, LINK_NAME_SPECIFIER, strlen(LINK_NAME_SPECIFIER)) == 0) {
            if (_parseLinkName(token, specifiedLinkName) != 0) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper connection name specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            linkName = specifiedLinkName;
            parcMemory_Deallocate(&token);
            continue;
        }

        if (strncasecmp(token, LOCAL_LINK_FLAG, strlen(LOCAL_LINK_FLAG)) == 0) {
            forceLocal = _parseLocalFlag(token);
            if (forceLocal == 0) {
                parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                              "Improper local specification (%s)", token);
                parcMemory_Deallocate(&token);
                errno = EINVAL;
                return NULL;
            }
            parcMemory_Deallocate(&token);
            continue;
        }

        parcLog_Error(athenaTransportLinkModule_GetLogger(athenaTransportLinkModule),
                      "Unknown connection parameter (%s)", token);
        parcMemory_Deallocate(&token);
        errno = EINVAL;
        return NULL;
    }

    if (isListener) {
        result = _ETHOpenListener(athenaTransportLinkModule, linkName, device, &srcMAC, mtu);
    } else {
        if (srcMACSpecified) {
            result = _ETHOpenConnection(athenaTransportLinkModule, linkName, device, &srcMAC, &destMAC, mtu);
        } else {
            result = _ETHOpenConnection(athenaTransportLinkModule, linkName, device, NULL, &destMAC, mtu);
        }
    }

    // forced IsLocal/IsNotLocal, mainly for testing
    if (result && forceLocal) {
        athenaTransportLink_ForceLocal(result, forceLocal);
    }

    return result;
}
コード例 #24
0
ファイル: athena.c プロジェクト: chris-wood/ghost
static void
_processInterest(Athena *athena, CCNxInterest *interest, PARCBitVector *ingressVector)
{
    uint8_t hoplimit;

    //
    // *   (0) Hoplimit check, exclusively on interest messages
    //
    int linkId = parcBitVector_NextBitSet(ingressVector, 0);
    if (athenaTransportLinkAdapter_IsNotLocal(athena->athenaTransportLinkAdapter, linkId)) {
        hoplimit = ccnxInterest_GetHopLimit(interest);
        if (hoplimit == 0) {
            // We should never receive a message with a hoplimit of 0 from a non-local source.
            parcLog_Error(athena->log,
                          "Received a message with a hoplimit of zero from a non-local source (%s).",
                          athenaTransportLinkAdapter_LinkIdToName(athena->athenaTransportLinkAdapter, linkId));
            return;
        }
        ccnxInterest_SetHopLimit(interest, hoplimit - 1);
    }

    //
    // *   (1) if the interest is in the ContentStore, reply and return,
    //     assuming that other PIT entries were satisified when the content arrived.
    //
    CCNxMetaMessage *content = athenaContentStore_GetMatch(athena->athenaContentStore, interest);
    if (content) {
        const char *ingressVectorString = parcBitVector_ToString(ingressVector);
        parcLog_Debug(athena->log, "Forwarding content from store to %s", ingressVectorString);
        parcMemory_Deallocate(&ingressVectorString);
        PARCBitVector *result = athenaTransportLinkAdapter_Send(athena->athenaTransportLinkAdapter, content, ingressVector);
        if (result) { // failed channels - client will resend interest unless we wish to optimize things here
            parcBitVector_Release(&result);
        }
        return;
    }

    //
    // *   (2) add it to the PIT, if it was aggregated or there was an error we're done, otherwise we
    //         forward the interest.  The expectedReturnVector is populated with information we get from
    //         the FIB and used to verify content objects ingress ports when they arrive.
    //
    PARCBitVector *expectedReturnVector;
    AthenaPITResolution result;
    if ((result = athenaPIT_AddInterest(athena->athenaPIT, interest, ingressVector, &expectedReturnVector)) != AthenaPITResolution_Forward) {
        if (result == AthenaPITResolution_Error) {
            parcLog_Error(athena->log, "PIT resolution error");
        }
        return;
    }

    // Divert interests destined to the forwarder, we assume these are control messages
    CCNxName *ccnxName = ccnxInterest_GetName(interest);
    if (ccnxName_StartsWith(ccnxName, athena->athenaName) == true) {
        _processInterestControl(athena, interest, ingressVector);
        return;
    }

    //
    // *   (3) if it's in the FIB, forward, then update the PIT expectedReturnVector so we can verify
    //         when the returned object arrives that it came from an interface it was expected from.
    //         Interest messages with a hoplimit of 0 will never be sent out by the link adapter to a
    //         non-local interface so we need not check that here.
    //
    ccnxName = ccnxInterest_GetName(interest);
    PARCBitVector *egressVector = athenaFIB_Lookup(athena->athenaFIB, ccnxName);

    if (egressVector != NULL) {
        // Remove the link the interest came from if it was included in the FIB entry
        parcBitVector_ClearVector(egressVector, ingressVector);
        // If no links remain, send a no route interest return message
        if (parcBitVector_NumberOfBitsSet(egressVector) == 0) {
            CCNxInterestReturn *interestReturn = ccnxInterestReturn_Create(interest, CCNxInterestReturn_ReturnCode_NoRoute);
            PARCBitVector *result = athenaTransportLinkAdapter_Send(athena->athenaTransportLinkAdapter, interestReturn, ingressVector);
            parcBitVector_Release(&result);
            ccnxInterestReturn_Release(&interestReturn);
        } else {
            parcBitVector_SetVector(expectedReturnVector, egressVector);
            PARCBitVector *result = athenaTransportLinkAdapter_Send(athena->athenaTransportLinkAdapter, interest, egressVector);
            if (result) { // remove failed channels - client will resend interest unless we wish to optimize here
                parcBitVector_ClearVector(expectedReturnVector, result);
                parcBitVector_Release(&result);
            }
        }
    } else {
        // No FIB entry found, return a NoRoute interest return and remove the entry from the PIT.
        CCNxInterestReturn *interestReturn = ccnxInterestReturn_Create(interest, CCNxInterestReturn_ReturnCode_NoRoute);
        PARCBitVector *result = athenaTransportLinkAdapter_Send(athena->athenaTransportLinkAdapter, interestReturn, ingressVector);
        parcBitVector_Release(&result);
        ccnxInterestReturn_Release(&interestReturn);
        const char *name = ccnxName_ToString(ccnxName);
        if (athenaPIT_RemoveInterest(athena->athenaPIT, interest, ingressVector) != true) {
            parcLog_Error(athena->log, "Unable to remove interest (%s) from the PIT.", name);
        }
        parcLog_Debug(athena->log, "Name (%s) not found in FIB and no default route. Message dropped.", name);
        parcMemory_Deallocate(&name);
    }
}
コード例 #25
0
static AthenaTransportLinkModule *
_LoadModule(AthenaTransportLinkAdapter *athenaTransportLinkAdapter, const char *moduleName)
{
    assertTrue(_LookupModule(athenaTransportLinkAdapter, moduleName) == NULL,
               "attempt to load an already loaded module");

    // Derive the entry initialization name from the provided module name
    const char *moduleEntry;
    moduleEntry = _moduleNameToInitMethod(moduleName);

    // Check to see if the module was statically linked in.
    void *linkModule = RTLD_DEFAULT;
    ModuleInit _init = dlsym(linkModule, moduleEntry);

    // If not statically linked in, look for a shared library and load it from there
    if (_init == NULL) {
        // Derive the library name from the provided module name
        const char *moduleLibrary;
        moduleLibrary = _moduleNameToLibrary(moduleName);

        void *linkModule = dlopen(moduleLibrary, RTLD_NOW | RTLD_GLOBAL);
        parcMemory_Deallocate(&moduleLibrary);

        // If the shared library wasn't found, look for the symbol in our existing image.  This
        // allows a link module to be linked directly into Athena without modifying the forwarder.
        if (linkModule == NULL) {
            parcLog_Error(athenaTransportLinkAdapter_GetLogger(athenaTransportLinkAdapter),
                          "Unable to dlopen %s: %s", moduleName, dlerror());
            parcMemory_Deallocate(&moduleEntry);
            errno = ENOENT;
            return NULL;
        }

        _init = dlsym(linkModule, moduleEntry);
        if (_init == NULL) {
            parcLog_Error(athenaTransportLinkAdapter_GetLogger(athenaTransportLinkAdapter),
                          "Unable to find %s module _init method: %s", moduleName, dlerror());
            parcMemory_Deallocate(&moduleEntry);
            dlclose(linkModule);
            errno = ENOENT;
            return NULL;
        }
    }
    parcMemory_Deallocate(&moduleEntry);

    // Call the initialization method.
    PARCArrayList *moduleList = _init();
    if (moduleList == NULL) { // if the init method fails, unload the module if it was loaded
        parcLog_Error(athenaTransportLinkAdapter_GetLogger(athenaTransportLinkAdapter),
                      "Empty module list returned from %s module", moduleName);
        if (linkModule != RTLD_DEFAULT) {
            dlclose(linkModule);
        }
        errno = ENOENT;
        return NULL;
    }

    // Process each link module instance (typically only one)
    for (int index = 0; index < parcArrayList_Size(moduleList); index++) {
        AthenaTransportLinkModule *athenaTransportLinkModule = parcArrayList_Get(moduleList, index);
        _AddModule(athenaTransportLinkAdapter, athenaTransportLinkModule);
    }
    parcArrayList_Destroy(&moduleList);

    return _LookupModule(athenaTransportLinkAdapter, moduleName);
}
コード例 #26
0
ファイル: athena_main.c プロジェクト: chris-wood/ghost
static void
_parseCommandLine(Athena *athena, int argc, char **argv)
{
    int c;
    bool interfaceConfigured = false;

    while ((c = getopt_long(argc, argv, "hs:c:vd", options, NULL)) != -1) {
        switch (c) {
            case 's': {
                int sizeInMB = atoi(optarg);
                if (athenaContentStore_SetCapacity(athena->athenaContentStore, sizeInMB) != true) {
                    parcLog_Error(athena->log, "Unable to resize content store to %d (MB)", sizeInMB);
                }
                _contentStoreSizeInMB = sizeInMB;
                break;
            }
            case 'c': {
                PARCURI *connectionURI = parcURI_Parse(optarg);
                const char *result = athenaTransportLinkAdapter_Open(athena->athenaTransportLinkAdapter, connectionURI);
                if (result == NULL) {
                    parcLog_Error(athena->log, "Unable to configure %s: %s", optarg, strerror(errno));
                    parcURI_Release(&connectionURI);
                    exit(EXIT_FAILURE);
                }
                parcURI_Release(&connectionURI);
                interfaceConfigured = true;
                break;
            }
            case 'v':
                printf("%s\n", athenaAbout_Version());
                exit(0);
            case 'd':
                athenaTransportLinkAdapter_SetLogLevel(athena->athenaTransportLinkAdapter, PARCLogLevel_Debug);
                parcLog_SetLevel(athena->log, PARCLogLevel_Debug);
                break;
            case 'h':
            default:
                _usage();
                exit(EXIT_FAILURE);
                break;
        }
    }

    if (argc - optind) {
        parcLog_Error(athena->log, "Bad arguments");
        _usage();
        exit(EXIT_FAILURE);
    }

    if (interfaceConfigured != true) {
        PARCURI *connectionURI = parcURI_Parse(_athenaDefaultConnectionURI);
        if (athenaTransportLinkAdapter_Open(athena->athenaTransportLinkAdapter, connectionURI) == NULL) {
            parcLog_Error(athena->log, "Unable to configure an interface.  Exiting...");
            parcURI_Release(&connectionURI);
            exit(EXIT_FAILURE);
        }
        parcURI_Release(&connectionURI);
        struct utsname name;
        if (uname(&name) == 0) {
            char nodeURIspecification[MAXPATHLEN];
            sprintf(nodeURIspecification, "tcp://%s:%d/listener",
                    name.nodename, AthenaDefaultListenerPort);
            PARCURI *nodeURI = parcURI_Parse(nodeURIspecification);
            if (athenaTransportLinkAdapter_Open(athena->athenaTransportLinkAdapter, nodeURI) == NULL) {
                parcURI_Release(&nodeURI);
            }
        }
    }
}
コード例 #27
0
static int
_ETHSend(AthenaTransportLink *athenaTransportLink, CCNxMetaMessage *ccnxMetaMessage)
{
    struct _ETHLinkData *linkData = athenaTransportLink_GetPrivateData(athenaTransportLink);

    if (ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage) == CCNxTlvDictionary_SchemaVersion_V0) {
        parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink),
                      "sending deprecated version %d message\n", ccnxTlvDictionary_GetSchemaVersion(ccnxMetaMessage));
    }

    // Construct our header to prepend
    struct ether_header header;
    memcpy(header.ether_shost, &(linkData->link.myAddress), ETHER_ADDR_LEN * sizeof(uint8_t));
    memcpy(header.ether_dhost, &(linkData->link.peerAddress), ETHER_ADDR_LEN * sizeof(uint8_t));
    header.ether_type = htons(athenaEthernet_GetEtherType(linkData->athenaEthernet));

    // An iovec to contain the header and packet data
    struct iovec iov[2];
    struct iovec *array = iov;
    int iovcnt = 2;
    size_t messageLength = 0;

    // If the iovec we're prepending to has more than one element, allocatedIovec holds the
    // allocated IO vector of the right size that we must deallocate before returning.
    struct iovec *allocatedIovec = NULL;

    // Attach the header and populate the iovec

    CCNxCodecNetworkBufferIoVec *iovec = athenaTransportLinkModule_GetMessageIoVector(ccnxMetaMessage);

    iovcnt = ccnxCodecNetworkBufferIoVec_GetCount((CCNxCodecNetworkBufferIoVec *) iovec);
    const struct iovec *networkBufferIovec = ccnxCodecNetworkBufferIoVec_GetArray((CCNxCodecNetworkBufferIoVec *) iovec);

    // Trivial case, single iovec element.
    if (iovcnt == 1) {
        // Header
        array[0].iov_len = sizeof(struct ether_header);
        array[0].iov_base = &header;

        // Message content
        array[1].iov_len = networkBufferIovec->iov_len;
        array[1].iov_base = networkBufferIovec->iov_base;
        messageLength = array[0].iov_len + array[1].iov_len;
    } else {
        // Allocate a new iovec if more than one vector
        allocatedIovec = parcMemory_Allocate(sizeof(struct iovec) * (iovcnt + 1));
        array = allocatedIovec;

        // Header
        array[0].iov_len = sizeof(struct ether_header);
        array[0].iov_base = &header;
        messageLength = array[0].iov_len;

        // Append message content
        for (int i = 0; i < iovcnt; i++) {
            array[i + 1].iov_len = networkBufferIovec[i].iov_len;
            array[i + 1].iov_base = networkBufferIovec[i].iov_base;
            messageLength += array[i + 1].iov_len;
        }
    }
    iovcnt++; // increment for the header

    if (linkData->link.mtu) {
        if (messageLength > linkData->link.mtu) {
            if (allocatedIovec != NULL) {
                parcMemory_Deallocate(&allocatedIovec);
            }
            ccnxCodecNetworkBufferIoVec_Release(&iovec);
            errno = EMSGSIZE;
            return -1;
        }
    }

    parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink),
                  "sending message (size=%d)", messageLength);

    ssize_t writeCount = 0;
    writeCount = athenaEthernet_Send(linkData->athenaEthernet, array, iovcnt);
    ccnxCodecNetworkBufferIoVec_Release(&iovec);

    // Free up any storage allocated for a non-singular iovec
    if (allocatedIovec != NULL) {
        parcMemory_Deallocate(&allocatedIovec);
        array = NULL;
    }

    // on error close the link, else return to retry a zero write
    if (writeCount == -1) {
        if ((errno == EAGAIN) || (errno == EINTR)) {
            parcLog_Info(athenaTransportLink_GetLogger(athenaTransportLink), "send retry");
            linkData->_stats.send_Retry++;
            return -1;
        }
        athenaTransportLink_SetEvent(athenaTransportLink, AthenaTransportLinkEvent_Error);

        parcLog_Error(athenaTransportLink_GetLogger(athenaTransportLink),
                      "send error (%s)", strerror(errno));
        linkData->_stats.send_Error++;
        return -1;
    }

    // Short write
    if (writeCount != messageLength) {
        linkData->_stats.send_ShortWrite++;
        parcLog_Debug(athenaTransportLink_GetLogger(athenaTransportLink), "short write");
        return -1;
    }

    return 0;
}